目录
一、前言
二、父工程搭建
三、公共API项目搭建
四、Eureka Server端搭建
五、Eureka Client【服务提供者】搭建
六、Eureka Client【服务消费者】搭建
七、扩展
由于SpringCloud较老版本已经停止更新,已经迭代好几个版本,加上最近SpringCloud Alibaba挺火的,所以想花一点时间去学习一下并总结一番,本文主要搭建Spring Cloud Eureka服务注册中心(单节点),在生产环境中,基本上都是多节点负载均衡,形成一个服务注册中心集群,这样一个注册中心崩了不会影响整个系统的运行以及接口的调用。
以前已经对老版本的实现方式做了一些总结,有兴趣的小伙伴可以学习一下:https://blog.csdn.net/Weixiaohuai/article/details/82498822
版本说明:
SpringBoot:2.2.2.RELEASE
SpringCloud:Hoxton.SR1
由于后面可能会学习Eureka多节点集群搭建、Ribbon负载均衡和SpringCloud Alibaba相关知识,可能会涉及很多子项目,所以这里我们搭建一个父工程用来统一管理这些子module,下面我们可以通过IDEA工具创建一个maven项目。
然后配置maven,点击下一步即可创建完成。由于需要用此父工程对子module用到的依赖包的版本统一进行控制,所以我们需要调整pom.xml:
4.0.0
com.wsh.springcloud
springcloud2020
1.0-SNAPSHOT
springcloud-provider-payment8001
springcloud-consumer-order80
springcloud-api-commons
springcloud-eureka-server7001
springcloud-eureka-server7002
springcloud-provider-payment8002
pom
UTF-8
1.8
1.8
4.12
1.2.17
1.16.18
5.1.47
1.1.16
1.3.0
org.springframework.boot
spring-boot-dependencies
2.2.2.RELEASE
pom
import
org.springframework.cloud
spring-cloud-dependencies
Hoxton.SR1
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2.1.0.RELEASE
pom
import
mysql
mysql-connector-java
${mysql.version}
com.alibaba
druid
${druid.version}
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybatis.spring.boot.version}
junit
junit
${junit.version}
log4j
log4j
${log4j.version}
org.projectlombok
lombok
${lombok.version}
true
org.springframework.boot
spring-boot-maven-plugin
true
true
至此,父工程项目搭建完成。
注意,此module主要包含一些公共的实体类、工具类,然后我们结合mvn clean和mvn install后,可以供其他子模块进行在pom.xml中进行引用,避免重复在每个项目中进行声明。
项目名称:springcloud-api-commons
【a】pom.xml
springcloud2020
com.wsh.springcloud
1.0-SNAPSHOT
4.0.0
springcloud-api-commons
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
cn.hutool
hutool-all
5.1.0
【b】定义统一结果返回类
package com.wsh.springcloud.common;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description 统一返回结果包装类
* @Date 2020/7/26 9:50
* @Author weishihuai
* 说明:
*/
@Data
@NoArgsConstructor
public class JsonResult {
/**
* 状态码
*/
private Integer code;
/**
* 返回信息
*/
private String msg;
/**
* 返回数据
*/
private T data;
public JsonResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
this.data = null;
}
public JsonResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
}
【c】定义实体类,后面用到
package com.wsh.springcloud.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description 支付实体类
* @Date 2020/7/26 9:47
* @Author weishihuai
* 说明:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment {
private Long pkid;
private String serial;
}
然后我们先执行mvn clean, clean完成后再执行mvn install安装到仓库中,这样别的微服务项目在pom.xml中就可以通过springcloud-api-commons项目的G、A、V坐标引入公共魔块。
在别的微服务中引入的方式如下所示:
com.wsh.springcloud
springcloud-api-commons
${project.version}
在父工程的基础上创建一个子module,加入 Eureka-server的依赖。
【a】pom.xml
springcloud2020
com.wsh.springcloud
1.0-SNAPSHOT
4.0.0
springcloud-eureka-server7001
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
com.wsh.springcloud
springcloud-api-commons
${project.version}
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
test
junit
junit
此处需要注意的是,新版本Eureka Server引入的依赖包为:
spring-cloud-starter-netflix-eureka-server
【b】application.yml
server:
port: 7001
spring:
application:
name: springcloud-eureka-server #应用名称
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
fetch-registry: false #false表示不需要去检索服务实例
register-with-eureka: false #false表示不向注册中心注册自己
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
【c】主启动类加上@EnableEurekaServer注解,开启Eureka服务发现的功能。
/**
* @Description Eureka服务注册中心
* @Date 2020/7/26 8:55
* @Author weishihuai
* 说明:
*/
@SpringBootApplication
//@EnableEurekaServer: 开启Eureka服务注册和发现功能(服务端)
@EnableEurekaServer
public class SpringCloudEurekaServiceApplication7001 {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaServiceApplication7001.class, args);
}
}
【d】启动项目,访问localhost:1111/
可见,此时并没有一个微服务客户端注册上来,显示no instance available暂无可用实例。下面我们搭建一个Eureka客户端注册上来。
新建子模块【springcloud-provider-payment8001】,项目结构如下图:
【a】pom.xml
springcloud2020
com.wsh.springcloud
1.0-SNAPSHOT
4.0.0
springcloud-provider-payment8001
com.wsh.springcloud
springcloud-api-commons
${project.version}
org.springframework.cloud
spring-cloud-starter-zipkin
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.mybatis.spring.boot
mybatis-spring-boot-starter
com.alibaba
druid-spring-boot-starter
1.1.10
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-jdbc
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
【b】application.yml
server:
port: 8001 #指定服务端口号
spring:
application:
name: springcloud-payment-service #指定服务名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.jdbc.Driver # mysql驱动包&useSSL=false
url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf-8 #数据库地址
username: root #数据库用户名
password: root #数据库用户密码
mybatis:
mapperLocations: classpath:mapper/*.xml #mapper.xml文件扫描位置
type-aliases-package: com.wsh.springcloud.entity # 所有Entity别名类所在包
eureka:
client:
register-with-eureka: true #注册到Eureka注册中心
fetch-registry: true #开启检索服务
service-url:
defaultZone: http://localhost:7001/eureka/ #单机版Eureka注册中心
【c】启动类加上@EnableEurekaClient注解,表示向Eureka Server注册中心注册自己。
package com.wsh.springcloud;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @Description 主启动类
* @Date 2020/7/25 9:39
* @Author weishihuai
* 说明:
*/
@SpringBootApplication
@EnableEurekaClient
//@EnableDiscoveryClient
@MapperScan("com.wsh.springcloud.mapper")
public class PaymentServiceApplication8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentServiceApplication8001.class, args);
}
}
【d】由于需要使用mysql数据库,下面是建表sql:
CREATE TABLE `payment` (
`pkid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`serial_number` varchar(200) DEFAULT NULL COMMENT '流水号',
PRIMARY KEY (`pkid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
【e】以下就是一些Controller、Service、Mapper接口和Mapper.xml定义,比较简单,这里就不过多说明。
(一)、PaymentMapper.xml
insert into payment(serial) values(#{serial});
(二)、PaymentMapper
package com.wsh.springcloud.mapper;
import com.wsh.springcloud.entity.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @Description Mapper持久层接口
* @Date 2020/7/25 9:53
* @Author weishihuai
* 说明:
*/
@Mapper
public interface PaymentMapper {
int save(Payment payment);
Payment getPaymentById(@Param("pkid") Long pkid);
}
(三)、PaymentService
package com.wsh.springcloud.service;
import com.wsh.springcloud.entity.Payment;
public interface PaymentService {
int save(Payment payment);
Payment getPaymentById(Long id);
}
(四)、PaymentServiceImpl
package com.wsh.springcloud.service.impl;
import com.wsh.springcloud.entity.Payment;
import com.wsh.springcloud.mapper.PaymentMapper;
import com.wsh.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PaymentServiceImpl implements PaymentService {
@Resource
private PaymentMapper paymentMapper;
public int save(Payment payment) {
return paymentMapper.save(payment);
}
public Payment getPaymentById(Long id) {
return paymentMapper.getPaymentById(id);
}
}
(五)、PaymentController
package com.wsh.springcloud.controller;
import com.wsh.springcloud.common.JsonResult;
import com.wsh.springcloud.entity.Payment;
import com.wsh.springcloud.service.PaymentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;
@RestController
public class PaymentController {
private static final Logger logger = LoggerFactory.getLogger(PaymentController.class);
@Autowired
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@PostMapping(value = "/payment/save")
public JsonResult save(@RequestBody Payment payment) {
int result = paymentService.save(payment);
logger.info("*****插入结果:" + result);
if (result > 0) {
return new JsonResult(200, "插入数据库成功,serverPort: " + serverPort, result);
} else {
return new JsonResult(444, "插入数据库失败", null);
}
}
@GetMapping(value = "/payment/get/{id}")
public JsonResult getPaymentById(@PathVariable("id") Long id) {
Payment payment = paymentService.getPaymentById(id);
if (payment != null) {
return new JsonResult(200, "查询成功,serverPort: " + serverPort, payment);
} else {
return new JsonResult(444, "没有对应记录,查询ID: " + id, null);
}
}
}
下面我们启动此项目,观察http://localhost:7001/:
可以看到eureka-client已经成功注册到Eureka上面。
新建子模块【springcloud-consumer-order80】,项目结构如下图所示:
【a】pm.xml
springcloud2020
com.wsh.springcloud
1.0-SNAPSHOT
4.0.0
springcloud-consumer-order80
com.wsh.springcloud
springcloud-api-commons
${project.version}
org.springframework.cloud
spring-cloud-starter-zipkin
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
【b】application.yml
server:
port: 80
spring:
application:
name: springcloud-order-service
eureka:
instance:
hostname: localhost
client:
register-with-eureka: true #注册到Eureka注册中心
fetch-registry: true #开启检索服务
service-url:
defaultZone: http://localhost:7001/eureka/ #单机版Eureka注册中心
【c】主启动类
package com.wsh.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class OrderServiceApplication80 {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication80.class, args);
}
}
【d】配置类
package com.wsh.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @Description 全局配置文件类
* @Date 2020/7/25 15:45
* @Author weishihuai
* 说明: 此处配置也可以放在主启动类中
*/
@Configuration
public class CustomConfig {
//注册到spring ioc容器中
@Bean
@LoadBalanced //开启负载均衡功能
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
【e】Controller
package com.wsh.springcloud.controller;
import com.wsh.springcloud.common.JsonResult;
import com.wsh.springcloud.entity.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@Slf4j
public class OrderController {
//支付服务提供者地址
// public static final String PAYMENT_URL = "http://localhost:8001";
//支付服务提供者注册到Eureka的application name
public static final String PAYMENT_URL = "http://SPRINGCLOUD-PAYMENT-SERVICE";
@Resource
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
@PostMapping("/consumer/payment/save")
public JsonResult savePayment(@RequestBody Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/save", payment, JsonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public JsonResult getPayment(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, JsonResult.class);
}
@GetMapping("/consumer/payment/getForEntity/{id}")
public JsonResult getPayment2(@PathVariable("id") Long id) {
ResponseEntity entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, JsonResult.class);
if (entity.getStatusCode().is2xxSuccessful()) {
return entity.getBody();
} else {
return new JsonResult<>(444, "操作失败");
}
}
}
启动项目,观察http://localhost:7001/:
可以看到eureka-client已经成功注册到Eureka上面。
接着我们使用postman测试一下,访问http://localhost:80/consumer/payment/get/3,如下图:
可见,已经实现了成功从服务消费者【order端】调用 服务提供者【payment端】的接口,这也说明我们Eureka Server成功搭建完成。
Eureka内部维护服务节点
的机制:
【a】服务下线
迭代更新
、终止访问
某一个或者多个服务节点
时,在正常关闭服务节点
的情况下,Eureka Client
会告诉Eureka Server
我要下线了,Eureka Server
收到请求后会将该服务实例
的运行状态
由UP
修改为DOWN
【b】失效剔除
Eureka Server
在启动完成后会创建一个定时器每隔60秒
检查一次服务健康状况
,如果其中一个服务节点超过90秒
未检查到心跳,那么Eureka Server
会自动从服务实例列表
内将该服务剔除
。(内存溢出
、杀死进程
、服务器宕机
等非正常流程关闭服务节点
)
【c】自我保护
Eureka Server
的自我保护机制
会检查最近15分钟
内所有Eureka Client
正常心跳的占比,如果低于85%
就会被触发保护机制,当触发自我保护机制
后Eureka Server
就会锁定服务列表
,不让服务列表内的服务过期
,不过这样我们在访问服务时,得到的服务很有可能是已经失效的实例
,如果是这样我们就会无法访问到期望的资源,会导致服务调用失败,所以这时我们就需要有对应的容错机制
、熔断机制
。
关闭自我保护: 在application.properties配置文件中加入代码:
eureka.server.enable-self-preservation=false
相关项目的代码我已经放在Gitee上,有需要的小伙伴可以去拉取进行学习:https://gitee.com/weixiaohuai/springcloud_Hoxton
以上就是单节点的Eureka服务注册中心搭建的详细过程,由于笔者水平有限,如有不对之处,还请小伙伴们指正,相互学习,一起进步。