Eureka注册中心
什么是Eureka
首先我们来解决第一问题,服务的管理。
问题分析
在原始的案例中,一个系统对外提供服务,需要对外暴露自己的地址。而调用者需要记录服务提供者的地址。将来地址出现变更,还需要及时更新。这在服务较少的时候并不觉得有什么,但是在现在日益复杂的互联网环境,一个项目肯定会拆分出十几,甚至数十个微服务。此时如果还人为管理地址,不仅开发困难,将来测试、发布上线都会非常麻烦,这与DevOps的思想是背道而驰的。
DevOps:是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。
网约车
这就好比是 网约车出现以前,人们出门叫车只能叫出租车。一些私家车想做出租却没有资格,被称为黑车。而很多人想要约车,但是无奈出租车太少,不方便。私家车很多却不敢拦,而且满大街的车,谁知道哪个才是愿意载人的。一个想要,一个愿意给,就是缺少引子,缺乏管理啊。
此时滴滴这样的网约车平台出现了,所有想载客的私家车全部到滴滴注册,记录你的车型(服务类型),身份信息(联系方式)。这样提供服务的私家车,在滴滴那里都能找到,一目了然。
此时要叫车的人,只需要打开APP,输入你的目的地,选择车型(服务类型),滴滴自动安排一个符合需求的车到你面前,为你服务,完美!
Eureka做了什么?
Eureka就好比是滴滴,负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。
同时,服务提供方与Eureka之间通过“心跳”机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。
这就实现了服务的自动注册、发现、状态监控。
原理图
基本架构:
- Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
- 提供者:启动后向Eureka注册自己信息(地址,提供什么服务)
- 消费者:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定期更新
- 心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态
以上就是Eureka的功能描述,下面我们在代码中感受一下Eureka的神秘之处
入门案例
编写EurekaServer
接下来我们创建一个项目,启动一个EurekaServer:
依然使用Spring提供的快速搭建工具:
步骤一:
步骤二:
步骤三:
xml配置:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- SpringCloud版本,F系列 -->
<spring-cloud.version>Finchley.RC1</spring-cloud.version>
</properties>
<dependencies>
<!-- Eureka服务端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- SpringCloud依赖,一定要放到dependencyManagement中,起到管理版本的作用即可 -->
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- SpringCloud 私服-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
启动器
@SpringBootApplication
@EnableEurekaServer // 声明这个应用是一个EurekaServer
public class EurekaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaDemoApplication.class, args);
}
}
编写配置application.yml:
注意:yml文件中不要按tap键
server:
port: 10086 # 端口
spring:
application:
name: eureka-server # 应用名称,会在Eureka中显示
eureka:
client:
register-with-eureka: false # 是否注册自己的信息到EurekaServer,默认是true
fetch-registry: false # 是否拉取其它服务的信息,默认是true
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要加上其它Server的地址。
defaultZone: http://127.0.0.1:${server.port}/eureka
启动服务
并访问:http://127.0.0.1:10086/eureka
当你看到这个页面是恭喜你eureka项目已经搭建 ok
yml文件详解
启动时的错误
解释:
这里的错误可以忽略,如果在配置文件中.yml文件中eureka.client.register-with-eureka:true
这是因为eureka自己注册到自己上面,但是自己还没有启动好,所以就报差错了,当自己启动好之后错误就取消了
为了方便运行便于查看端口号和可以启动的启动器和运行多个项目在这里介绍一下idea使用小技巧
- Run Dashboard
Run Dashboard 如何在控制台显示
https://blog.csdn.net/weixin_43201015/article/details/84992908 - 启动多个相同的项目
https://blog.csdn.net/weixin_43201015/article/details/84993162
Eureka环境搭建完毕,下面我们来个测试
创建一个user-service(服务)工程,将其注册到Eureka
搭建一个springboot项目
pom.xml文件的配置如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.43</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
<!-- 引入eureka客户端的jar包 -->
<!-- Eureka客户端 -->
<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</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
<!-- 局部配置:Spring私服 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
在启动器中添加@EnableEurekaClient
application.yml文件
#端口号
server:
port: 8090
spring:
#数据库配置
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
username: root
password: 1234
application:
name: user-service
profiles: userService1
#eureka配置
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
创建一个user-consumer(消费)工程,将其注册到Eureka
搭建方式和user-service相同,这里不再多说
pom.xml配置如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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>
<!-- 局部配置:Spring私服 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
application.yml文件
server:
port: 8088
spring:
application:
# 应用名称
name: user-consumer
eureka:
client:
# EurekaServer地址
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
# 当其它服务获取地址时提供ip而不是hostname
prefer-ip-address: true
# 指定自己的ip信息,不指定的话会自己寻找
ip-address: 127.0.0.1
在启动器中添加@EnableDiscoveryClient
配置完毕现在启动一下我的系统
1 、启动eureka注册中心,2、启动user-service服务系统,3、启动user-consumer消费系统
访问 http://localhost:10086/
可以看到服务系统和消费系统已经在注册中心了
下面我们来写一下测试代码
1.准备数据
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50549
Source Host : localhost:3306
Source Schema : test
Target Server Type : MySQL
Target Server Version : 50549
File Encoding : 65001
Date: 22/12/2018 09:42:31
*/
create database test
use test
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL,
`user_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`age` int(11) NULL DEFAULT NULL,
`sex` int(11) NULL DEFAULT NULL,
`birthday` date NULL DEFAULT NULL,
`created` date NULL DEFAULT NULL,
`updated` date NULL DEFAULT NULL,
`note` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES (1, 'tom', '123456', 'tom', 20, 1, '1999-11-29', '2018-11-29', '2018-11-29', NULL);
INSERT INTO `tb_user` VALUES (2, 'lucy', '123456', 'lucy', 20, 2, '2018-11-29', '2018-11-29', '2018-11-29', NULL);
SET FOREIGN_KEY_CHECKS = 1;
2.user-service内容编写
2.1 pojo
这里介绍一个小功能 Lombok
Lombok使用详情: https://blog.csdn.net/weixin_43201015/article/details/85196191
@Table(name = "tb_user")
@Data
@ToString
public class User {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String password;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 性 别,1男性,2女性
*/
private Integer sex;
/**
* 出生日期
*/
private Date birthday;
/**
* 创建时间
*/
private Date created;
/**
* 更新时间
*/
private Date updated;
/**
* 备注
*/
private String note;
}
2.2 Mapper
package com.czxy.dao;
import com.czxy.pojo.User;
import tk.mybatis.mapper.common.Mapper;
/**
* @Author 郭德峻
* @Email guodejun0808@163.com
* @Describe :通过通用mapper,处理service的请求
*/
@org.apache.ibatis.annotations.Mapper
public interface UserMapper extends Mapper<User> {
}
2.3 service
package com.czxy.service;
import com.czxy.dao.UserMapper;
import com.czxy.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @ClassName UserService
* @Author 郭德峻
* @Data 2018/12/3 20:15
* @Email guodejun0808@163.com
* @Describe:service处理controler的请求
*/
@Service
@Transactional
public class UserService {
@Autowired
private UserMapper userMapper;
/**
* @Author 郭德峻
* @Date 20:27 2018/12/3
* @Param id
* @return user
* @describe 调用mapper的selectByPrimaryKey的方法获得用户
*/
public User findById(Long id) {
return userMapper.selectByPrimaryKey(id);
}
}
2.4 controller
package com.czxy.controller;
import com.czxy.pojo.User;
import com.czxy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 郭德峻
* @Email guodejun0808@163.com
* @describe :为user-consumer提供服务
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* @author 郭德峻
* @param id
* @return user
* @describe 接收user-consumer的请求,并返回一个user
*/
@GetMapping("/{id}")
public User findById(@PathVariable("id") Long id){
//调用UserService的findById()方法
return userService.findById(id);
}
}
到此user-service服务方 编写完毕
3. user-consume
3.1启动类中注册RestTemplate
3.2 Mapper
注意,这里不是调用mapper查数据库,而是通过DiscoveryClient远程查询user-service中的接口:
@Component
public class UserDao {
@Autowired
private RestTemplate restTemplate;
// Eureka客户端,可以获取到服务实例信息
@Autowired
private DiscoveryClient discoveryClient;
public User queryUserById(Long id){
// 根据服务名称,获取服务实例
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
ServiceInstance instance = instances.get(0);
// 获取ip和端口信息
String baseUrl = "http://"+instance.getHost() + ":" + instance.getPort()+"/user/";
User user = this.restTemplate.getForObject(baseUrl + id, User.class);
return user;
}
}
3.2 service
@Service
public class UserService {
@Autowired
private UserDao userDao;
public List<User> querUserByIds(List<Long> ids){
List<User> users = new ArrayList<>();
for (Long id : ids) {
User user = this.userDao.queryUserById(id);
users.add(user);
}
return users;
}
}
3.2 Mapper
@RestController
@RequestMapping("consume")
public class ConsumerController {
@Autowired
private UserService userService;
@GetMapping
public List<User> consume(@RequestParam("ids") List<Long> ids) {
return this.userService.queryUserByIds(ids);
}
}