SpringCloud之服务注册与发现Eureka(一)

1,798 阅读16分钟

为什么使用springcloud呢?

最开始我们用的都是单一架构(all in one),为什么会演变成垂直架构应用(vertical application)呢,由于我们最开始的用户本来就少,10几年前20年前貌似电脑没有多少人家有的把,网页也都是静态页面,访问量根本不用担心,而且所有业务都在同一里面,当我们修改了一个地方,哪怕仅仅是一个页面的一个汉字,整个系统都要重新部署修改,系统模块之间耦合度太高,慢慢的就想到能不能把系统各个模块分开呢,也就出现了垂直架构,但是使用着慢慢也发现,尽管界面+业务逻辑实现了逻辑的分离,但是应用不可能完全独立,大量的应用之间需要交互,耦合度还是比较高,而且用户也逐渐增多,部署在一个机器上承受不了这么大的访问量,这也就出现了SOA架构,SOA架构对系统又进行了粒度细化,将一个大的系统服务拆分成多个子服务,部署到不同机器上,通过rpc或者webservice实现模块之间的交互,而现在逐渐演变的微服务架构(microservice),则是将服务更加颗粒化,使之耦合度更低,因此微服务是现在的趋势,而springcloud则是微服务的一个代表。

eureka注册中心

由于上面我们所说的,将一个大的系统服务拆分成多个小的子系统,那么系统之间通信就比较困难,我们以前通过webservice啥的进行通信,这样我们就需要知道要调用的服务的ip还有端口号信息,而且例如我子服务a知道b服务的ip端口,但是当b服务的ip或者端口修改后,我们还需要修改子服务a的信息,当我们系统足够大,并且子服务足够多的时候,系统之间通信就变得非常乱,不好维护,而eureka则解决了这一问题,所有子服务都将自己的ip端口信息都注册给eureka,由eureka帮我们实现统一管理,比如服务a需要与服务b进行通信,服务a不需要知道b服务的ip及端口了,因为服务b已经将自己的信息注册到了eureka上了,我们只需要在eureka上查就可以了,当服务b信息修改了,服务b会自动提交给eureka最新的信息,服务a完全不受影响。

搭建项目结构

在此之前,我们可以先把简单的项目结构搭建起来,无非就是用maven创建多模块项目,不会的可以参考maven多模块搭建步骤这篇文章,完成后大致结构如下图所示:

maven_children_finish_jiegoutu1.png
maven_children_finish_jiegoutu1.png

由于demo_parent作为整个项目的父项目,作用:同一项目版本和公共依赖的引入,所以先将依赖在pom文件中引入。

 1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0"
3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

5    <modelVersion>4.0.0</modelVersion>
6    <groupId>com.lytw13</groupId>
7    <artifactId>demo_parent</artifactId>
8    <packaging>pom</packaging>
9    <version>1.0-SNAPSHOT</version>
10    <modules>
11        <module>../demo_api</module>
12        <module>../demo_sys</module>
13        <module>../demo_configuration</module>
14        <module>../demo_web</module>
15    </modules>
16    <!-- 统一项目版本信息 -->
17    <properties>
18        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
19        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
20        <java.version>11</java.version>
21        <maven.compiler.source>11</maven.compiler.source>
22        <maven.compiler.target>11</maven.compiler.target>
23        <spring-boot-version>2.1.9.RELEASE</spring-boot-version>
24        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
25        <project.version>1.0-SNAPSHOT</project.version>
26    </properties>
27    <dependencyManagement>
28        <dependencies>
29            <!-- springboot -->
30            <dependency>
31                <groupId>org.springframework.boot</groupId>
32                <artifactId>spring-boot-dependencies</artifactId>
33                <version>${spring-boot-version}</version>
34                <type>pom</type>
35                <scope>import</scope>
36            </dependency>
37            <!-- springcloud -->
38            <dependency>
39                <groupId>org.springframework.cloud</groupId>
40                <artifactId>spring-cloud-dependencies</artifactId>
41                <version>${spring-cloud.version}</version>
42                <type>pom</type>
43                <scope>import</scope>
44            </dependency>
45        </dependencies>
46    </dependencyManagement>
47    <dependencies>
48        <!-- web -->
49        <dependency>
50            <groupId>org.springframework.boot</groupId>
51            <artifactId>spring-boot-starter-web</artifactId>
52        </dependency>
53        <!--  集成了测试相关依赖,例如junit -->
54        <dependency>
55            <groupId>org.springframework.boot</groupId>
56            <artifactId>spring-boot-starter-test</artifactId>
57            <scope>test</scope>
58        </dependency>
59        <!--监控信息展示-->
60        <dependency>
61            <groupId>org.springframework.boot</groupId>
62            <artifactId>spring-boot-starter-actuator</artifactId>
63        </dependency>
64        <!--json-->
65        <dependency>
66            <groupId>com.alibaba</groupId>
67            <artifactId>fastjson</artifactId>
68            <version>1.2.62</version>
69        </dependency>
70        <!-- email-->
71        <dependency>
72            <groupId>org.springframework.boot</groupId>
73            <artifactId>spring-boot-starter-mail</artifactId>
74        </dependency>
75    </dependencies>
76</project>

eureka注册中心搭建步骤

  1. 由上面的结构图可以看出,eureka_parent是所有项目的父项目,而demo_configuration则又是demo_eureka,demo_config、demo_zuul的父项目,所以可以将这3个子项目的依赖放在demo_configuration中。
 1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0"
3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

5    <parent>
6        <artifactId>demo_parent</artifactId>
7        <groupId>com.lytw13</groupId>
8        <version>1.0-SNAPSHOT</version>
9    </parent>
10    <modelVersion>4.0.0</modelVersion>
11    <artifactId>demo_configuration</artifactId>
12    <packaging>pom</packaging>
13    <modules>
14        <module>../demo_eurekaServer</module>
15        <module>../demo_config</module>
16        <module>../demo_zuul</module>
17    </modules>
18    <dependencies>
19        <!-- eureka服务端 -->
20        <dependency>
21            <groupId>org.springframework.cloud</groupId>
22            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
23        </dependency>
24    </dependencies>
25</project>
  1. demo_eureka用于eureka注册中心相关的配置。

(1) 添加eureka主启动类

 1package com.lytw13.demo;
2import org.springframework.boot.SpringApplication;
3import org.springframework.boot.autoconfigure.SpringBootApplication;
4import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
5
6@EnableEurekaServer
7@SpringBootApplication
8public class EurekaApplication {
9    public static void main(String[] args) {
10        SpringApplication.run(EurekaApplication.class,args);
11    }
12}

(2) 新建eureka配置文件application.yml。

 1server:
2  port: 6001
3
4spring:
5  application:
6    name: eureka
7eureka:
8  instance:
9    ip-address: true
10    instance-id: eureka01
11  client:
12    fetch-registry: false
13    register-with-eureka: false
14    service-url:
15      defaultZone: http://localhost:6001/eureka/    
  1. 启动eureka,测试是否成功。

(1) 启动项目。

springcloud_eureka_qidong.png
springcloud_eureka_qidong.png

(2) 启动完成后,访问配置的路径localhost:端口号,查看是否启动成功。
springcloud_eureka_ceshi.png
springcloud_eureka_ceshi.png

服务注册到eureka

现在尽管我们有了eureka注册中心,但是没有任何服务,我们需要将我们项目中的服务注册到注册中心上,这样我们才能实现服务之前的通信。

这里以demo_sys_user用户服务为例,其他步骤类似。因为demo_sys_user是demo_sys的子模块,所以我们只需要将依赖添加到demo_sys上即可,这样demo_sys下的其他子模块也就可以同时使用,比如连接数据库的相关依赖,这样就实现了依赖的统一管理。

  • demo_sys POM文件
 1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://maven.apache.org/POM/4.0.0"
3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

5    <parent>
6        <artifactId>demo_parent</artifactId>
7        <groupId>com.lytw13</groupId>
8        <version>1.0-SNAPSHOT</version>
9    </parent>
10    <modelVersion>4.0.0</modelVersion>
11
12    <artifactId>demo_sys</artifactId>
13    <packaging>pom</packaging>
14    <modules>
15        <module>../demo_sys_user</module>
16        <module>../demo_sys_role</module>
17        <module>../demo_sys_menu</module>
18        <module>../demo_sys_notice</module>
19    </modules>
20    <dependencies>
21        <!-- 自定义api -->
22        <dependency>
23            <groupId>com.lytw13</groupId>
24            <artifactId>demo_api</artifactId>
25            <version>1.0-SNAPSHOT</version>
26        </dependency>
27        <!-- eureka客户端 -->
28        <dependency>
29            <groupId>org.springframework.cloud</groupId>
30            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
31        </dependency>
32        <!-- 数据库相关 -->
33        <dependency>
34            <groupId>mysql</groupId>
35            <artifactId>mysql-connector-java</artifactId>
36        </dependency>
37        <dependency>
38            <groupId>org.mybatis.spring.boot</groupId>
39            <artifactId>mybatis-spring-boot-starter</artifactId>
40            <version>2.1.1</version>
41        </dependency>
42        <dependency>
43            <groupId>com.alibaba</groupId>
44            <artifactId>druid-spring-boot-starter</artifactId>
45            <version>1.1.20</version>
46        </dependency>
47    </dependencies>
48</project>
  • demo_sys_user添加主启动类
 1package com.lytw13.demo;
2
3import org.springframework.boot.SpringApplication;
4import org.springframework.boot.autoconfigure.SpringBootApplication;
5import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
6
7@SpringBootApplication
8@EnableEurekaClient  //开启eureka客户端
9public class UserApplication {
10    public static void main(String[] args) {
11        SpringApplication.run(UserApplication.class,args);
12    }
13}
  • demo_sys_user yml配置文件
 1server:
2  port: 7001
3
4spring:
5  application:
6    name: user
7  datasource:
8    driver-class-name: com.mysql.cj.jdbc.Driver
9    url: jdbc:mysql://localhost:3306/db_demo_sys?serverTimezone=Asia/Shanghai
10    username: root
11    password: 123456
12    type: com.alibaba.druid.pool.DruidDataSource
13    druid:
14      # 连接池的配置信息
15      # 初始化大小,最小,最大
16      initial-size: 5
17      min-idle: 5
18      maxActive: 20
19      # 配置获取连接等待超时的时间
20      maxWait: 60000
21      filters: stat,wall,slf4j
22
23mybatis:
24  mapper-locations: classpath:mapper/*.xml
25  config-location: classpath:mybatis/mybatis.xml
26
27
28
29info:
30  name: lytw13
31  web: lytw13.top
32eureka:
33  client:
34    service-url:
35      defaultZone: http://localhost:6001/eureka/
36    fetch-registry: true
37    register-with-eureka: true
38  instance:
39    instance-id: user01
40    ip-address: true
41
42logging:
43  level:
44    com:
45      lytw13:
46        microservice:
47          dao: debug

启动该项目,查看eureka上是否有了该服务。

springcloud_eureka_zhuce.png
springcloud_eureka_zhuce.png

可以看到user已经注册到了eureka上,其他服务注册相同,不再赘述。

eureka服务注册的大概结构如下:

springcloud_eureka_jiegoutu.png
springcloud_eureka_jiegoutu.png

尽管这样,还是需要修改一些内容,我们将服务注册到eureka上,不仅仅是为了注册到上面,而是为了让其他服务可以调用,而我们上面的项目结构图可以看到demo_api项目是为了将公共接口,实体类,工具类抽取出来,因此我们可以将userservice接口还有实体类工具类的创建到demo_api里,下面接口代码列在下面,实体类就不用了吧,方法的返回值BaseResult是自己封装好的,当然可以直接返回实体类或者list啥的,这里是为了统一返回结果,方便处理。

  • UserService
 1package com.lytw13.demo.service;
2
3import com.lytw13.demo.model.BaseResult;
4import org.springframework.web.bind.annotation.*;
5
6@RequestMapping(value = "/user")
7public interface UserService {
8    @GetMapping("/get/{id}")
9    public BaseResult get(@PathVariable("id") Integer id);
10    @GetMapping("/list")
11    public BaseResult list();
12}
  • BaseReuslt
 1package com.lytw13.demo.model;
2
3import lombok.*;
4
5@AllArgsConstructor
6@NoArgsConstructor
7@Getter
8@Setter
9@ToString
10public class BaseResult {
11    private Integer resultCode;
12    private String resultMsg;
13    private Object resultData;
14}
  • ResponseResult
 1package com.lytw13.demo.utils;
2
3
4import com.lytw13.demo.model.BaseResult;
5
6public class ResponseResult {
7    public BaseResult setResult(Integer resultCode, String resultMsg, Object resultData){
8        return new BaseResult(resultCode, resultMsg, resultData);
9    }
10    public BaseResult setResultSuccess(Object resultData) {
11        return  new BaseResult(200,"success",resultData);
12    }
13    public BaseResult setResultFail(String msg) {
14        return  new BaseResult(400,msg,null);
15    }
16}

接着我们只需要编写demo_sys_user类了,具体无非也是实现上面的UserService接口,通过usermapper调用数据库,这里不再赘述了。编写完成后启动项目,通过application.yml中设置的端口访问方法,看看能否访问成功。

user_ceshi1.png
user_ceshi1.png
user_ceshi2.png
user_ceshi2.png

以前我们通过ip加端口访问,可以通过spring中的自带的RestTemplete进行访问。
这里我们使用demo_web作为消费者,调用demo_sys_user服务。

 1package com.lytw13.demo.controller;
2
3import com.lytw13.demo.model.BaseResult;
4import org.springframework.beans.factory.annotation.Autowired;
5import org.springframework.web.bind.annotation.*;
6import org.springframework.web.client.RestTemplate;
7
8@RestController
9@RequestMapping("/user")
10public class UserController {
11    @Autowired
12    RestTemplate restTemplate;
13    @GetMapping("getUser")
14    public BaseResult getUser(Integer id) {
15        BaseResult baseResult = restTemplate.getForObject("http://localhost:7001/user/get/"+id, BaseResult.class);
16        return baseResult;
17    }
18}

我们可以看到端口还有ip都是暴露的,这样肯定是不好的,因此我们就需要添加Ribbon来实现本地负载均衡,将上面的ip端口用在eureka注册中心的服务名来写。

 1package com.lytw13.demo.controller;
2
3import com.lytw13.demo.model.BaseResult;
4import org.springframework.beans.factory.annotation.Autowired;
5import org.springframework.web.bind.annotation.*;
6import org.springframework.web.client.RestTemplate;
7
8@RestController
9@RequestMapping("/user")
10public class UserController {
11    @Autowired
12    RestTemplate restTemplate;
13    @GetMapping("getUser")
14    public BaseResult getUser(Integer id) {
15        BaseResult baseResult = restTemplate.getForObject("http://USER/user/get/"+id, BaseResult.class);
16        return baseResult;
17    }
18}

eureka服务之间调用的大概结构如下:

springcloud_eureka_diaoyong_jiegoutu.png
springcloud_eureka_diaoyong_jiegoutu.png