SpringCloud从入门到住院(一):服务注册发现与负载均衡


转载请注明来源
https://blog.csdn.net/SingingFisher/article/details/88660952


1、前言

大家好,我是脚气哥!作为一个只会ctrl c、crl v的菜逼,我将带领众多菜逼一起学习SpringCloud!
项目地址:https://gitee.com/SiningFish/zed ,大家可以前往查看。如果觉得讲的知识有帮助,希望可以给我点个star哦。


推荐图书

  • 《Spring Cloud微服务架构开发实战》
  • 《Spring Cloud与Docker微服务架构实战》

2、项目简介

项目结构参考了pig项目,不过pig文档收费,我实在是舍不得为了一个文档出600多块钱,有这闲钱我直接多买几本书不是美滋滋。这也是我写这篇教程的原因之一,记录一下自己学习SpringCloud的历程,也希望可以帮助到其他人,毕竟,一个小白来学习SpringCloud本身就是一脸懵逼,再去看一个没有文档的大项目,那更是懵逼2
作为一名LOL最强王者5选手,我选择以我32%胜率的、最喜欢的英雄 --zed 来给我们的项目命名。

无形装逼,最为致命! ----影流之主·劫

2.1 项目主要依赖

依赖版本
SpringBoot2.1.3.RELEASE
SpringCloudGreenwich.SR1

2.2 开发工具

IDEA

2.3 面向谁

由于我刚学SpringCloud的时候,也就只会一点Spring,AOP都不懂什么意思,SpringBoot也就知道怎么创建项目,所以这篇教程还是比较简单的,面向和我一样的小白菜逼。

3、知识点与注意点

3.1 eureka注册中心

  • eureka注册中心,作为eureka的服务器,它通过hashmap维护了注册到它上面的微服务们。
  • 可以把它想象成一个映射表,记录了微服务的名称和具体URL地址,微服务可以通过这个信息来互相调用。
  • 如果没有注册中心,当然也能互相调用,不过想象一下,2个微服务互相调用,就要记住彼此的url,100个呢?1000个呢?这么多不得把人烦死,干什么程序员,回去卖馒头不一样能赚钱。。。
  • 它通过各种机制,例如心跳检测、自我保护等等保障了运行。具体可以买书看,或者上网查。

3.2 服务提供者

  • 提供服务的微服务,通过注册到eureka上给服务消费者使用

3.3 服务消费者

  • 消费服务的微服务

3.3 提供者与消费者的联系

  • 一个微服务可以同时作为消费者和提供者存在。例如B消费A,同时B提供给C消费。
  • 服务提供者必须注册到eureka服务器上,而消费者则不一定需要,就像eureka服务器本身一样,如果不做eureka集群,那么eureka本身是不需要注册到eureka上的

3.4 负载均衡

  • 很好理解,一个user服务,可以失效无法服务,多开几个user服务,端口不同,甚至是ip不同,那么一个坏了其他的仍然可以提供服务。

4、创建项目

你可以直接把我的gitee上代码clone下来,也可以自己创建。本文档对应a1分支,下载直接用https://gitee.com/SiningFish/zed/tree/a1/,然后用IDEA打开就可以了。下面将以直接自己创建的方式来讲解。

4.1创建父工程

直接IDEA创建一个普通的maven项目(建议刚学习不要选啥杂七杂八乱七八糟的依赖,缺啥自己填啥吧),我这里命名为zed。pom.xml文件如下:

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

    <groupId>com.origin</groupId>
    <artifactId>zed</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>${project.artifactId}</name>
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>

        <spring-boot.version>2.1.3.RELEASE</spring-boot.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
        <spring-cloud-ribbon.version>1.3.2.RELEASE</spring-cloud-ribbon.version>

        <mybatis-spring.version>1.3.2</mybatis-spring.version>

    </properties>

    <dependencies>
        <!--eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <!--springboot 测试依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <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>
        <finalName>${project.name}</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <target>${maven.compiler.target}</target>
                    <source>${maven.compiler.source}</source>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
        </plugins>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${spring-boot.version}</version>
                    <configuration>
                        <finalName>${project.build.finalName}</finalName>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </pluginManagement>

    </build>

</project>

稍微讲一下吧。

  1. 所有子模块需要公用的或者常用的依赖项版本都在父pom文件中定义,方便以后统一调整
  2. 两个plugin一个是maven编译插件,一个是springboot插件,没啥好说的。
  3. dependencyManagement中定义了springboot和springcloud的通用依赖,这样dependencies以及子模块中的dependencies中的依赖项,如果在spring-boot-dependenciesspring-cloud-dependencies已经有了的,就不用再指定版本了,不在的仍然要指定版本。
  4. 由于每个模块都是一个eureka客户端,所有这个父pom中定义了spring-cloud-starter-netflix-eureka-client依赖项。
  5. lombok是一个非常好用的简化代码插件和依赖,除了在pom中声明它的依赖,还要在IDEA中安装一下lombok插件,具体网上搜一下,是很简单的。lombok稍微去网上查一下用法,也是非常简单的,举例来说,你原本写个bean要写它的构造函数,现在你只要在类前面加个lombok的@AllArgsConstructor 注解,就不需要写构造函数了,到时候编译的时候会自动生成。比如,我的一个类,源码是这样的:
@RestController
@RequestMapping("/auth")
@AllArgsConstructor
public class AuthController {
    private UserService userService;
    //......
}

没有构造函数。它编译之后就成了这样:

@RestController
@RequestMapping({"/auth"})
public class AuthController {
    private UserService userService;
    public AuthController(UserService userService) {
        this.userService = userService;
    }
    //......
}

很流弊对吧,由于构造函数自动注入,甚至UserService都不要@Autowired了。

4.2 创建eureka服务器

直接在zed项目上右键,创建一个新的普通maven模块zed-eureka就好了。

4.2.1 pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>zed</artifactId>
        <groupId>com.origin</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>zed-eureka</artifactId>
    <packaging>jar</packaging>

    <description>eureka注册服务中心</description>

    <dependencies>
        <!--eureka注册服务中心-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

真简单啊,到时候maven导入依赖之后,zed-eureka除了有父pom中定义的那些依赖之外,还有spring-cloud-starter-netflix-eureka-server这个依赖,说明它是一个eureka服务器。

4.2.2 bootstrap.yml文件

server:
  port: 8762 #端口号

spring:
  application:
    name: zed-eureka  # 应用名

eureka:
  instance:
    hostname: ${spring.application.name}  
    # hostname名,通常是localhost或者主机地址,这里直接用的zed-eureka,是因为后面要配置hosts文件,具体后面再看
    prefer-ip-address: true  # 以IP注册到eureka服务器上(因为eureka服务器自己也同时是个eureka客户端)
  client:
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/  # eureka服务器地址
    register-with-eureka: false #不需要注册到eureka的可用服务列表里面,只需要简单的注册上就可以了
    fetch-registry: false # 不需要从eureka服务器上拉取可用服务列表,因为本身它就有了
  server:
    eviction-interval-timer-in-ms: 4000 #40秒钟踢出关闭的微服务
    enable-self-preservation: false  #关闭eureka自我保护

4.2.3 主程序

@EnableEurekaServer
@SpringBootApplication
public class ZedEurekaApplication {
    public static void main(String args[]){
        SpringApplication.run(ZedEurekaApplication.class, args);
    }
}

更加简单了,只不过是在原本的@SpringBootApplication启动注解上又加了一个@EnableEurekaServer注解,表示它是一个eureka服务器。现在已经可以直接运行eureka服务器了,运行完毕之后浏览器访问localhost:8762(端口号在bootstrap.yml中配置的),看到如下画面说明eureka服务器成功:
在这里插入图片描述

4.3 创建服务提供者

直接在zed项目上右键,创建一个新的普通maven模块zed-service-user就好了。

4.2.1 pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>zed</artifactId>
        <groupId>com.origin</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>zed-service-user</artifactId>
    <packaging>jar</packaging>

    <description>zed 用户服务</description>

    <dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring.version}</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

加了mybatis的依赖以及mysql的驱动包。

4.2.2 bootstrap.yml文件

server:
  port: 20000

spring:
  application:
    name: zed-service-user
eureka:
  instance:
    hostname: ${spring.application.name}
    prefer-ip-address: true
    instance-id: ${eureka.instance.hostname}:${server.port}
  client:
    service-url:
      defaultZone: http://zed-eureka:8762/eureka/

4.2.3 application.yml文件

#mybatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml

#数据库配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/zed?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
    username: root
    password: root

配置了数据库信息以及mybatis的映射文件地址信息。
bootstrap.yml将会先于application.yml加载,就目前而言这样分成两个配置文件是多余的,不过后面用处就大了。

4.2.4 其他通用代码

代码太多了我就不贴了,贴了占篇幅太大,而且都是些通用的东西,大家直接拷贝我的源码下来对照着看就好了。无非也就是些增删改查的东西。我直接讲一下目录结构就好了。
在这里插入图片描述

  • common:统一定义的常量类
    • Constant:接口,里面放的是controller返回值的自定义码。比如Integer SUCCESS=1定义了成功返回时的自定义码
    • Message: 接口,定义了返回的消息,如"登录成功!"
  • controller:控制器
    • UserController: 用户实体类相关控制器
  • domain:实体类
    • User:用户类
  • dto:数据传输对象
    • UserDtoUser对应的dto
    • Result:封装的统一返回对象,包含了自定义返回码code,消息msg,值data三个属性。到时候就不用这个controller返回User,那个controller返回Student了,全部弄成统一的,你看了也舒服,前端看了也舒服,大家都美滋滋的,省的成天提心吊胆,担心自己写的接口太烂了,被前端打。
  • mapper:mybatis实体类对应的mapper
    • UserMapperUser对应的mapper
  • service
    • UserServiceUserServiceImpl:用户Service
  • resources/mapper/UserMapper.xml:mapper对应的xml文件
  • resources/application.xml:已说明
  • resources/banner.txt:横幅
  • resources/bootstrap.yml:已说明

4.2.5 主程序

@EnableEurekaClient
@SpringBootApplication
@MapperScan("com.origin.zed.service.user.mapper") //指定mybatis的mapper接口地址
public class ZedUserServiceApplication {
    public static void main(String args[]){
        SpringApplication.run(ZedUserServiceApplication.class, args);
    }
}

4.3 创建服务消费者

直接在zed项目上右键,创建一个新的普通maven模块zed-service-consumer就好了。

4.3.1 pom.xml文件

不再累述了,只是添加了一个

<dependency>
	 <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-ribbon</artifactId>
     <version>${spring-cloud-ribbon.version}</version>
</dependency>

ribbon依赖。由于父pom中的spring-cloud-dependencies没有ribbon,所以需要在父pom中指定ribbon的版本

<spring-cloud-ribbon.version>1.3.2.RELEASE</spring-cloud-ribbon.version>

ribbon可以用来做负载均衡,到时候可以开多个服务提供者,服务消费者调用服务提供者时,将轮流访问各个服务提供者的实例。

4.3.2 bootstrap.yml文件

不再累述,实在是简单

4.3.3 其他通用代码

在这里插入图片描述

  • commondto:同服务提供者中的定义,可以直接写在一个子模块中,然后其他需要用到的子模块都依赖一下这个模块。现在暂时无所谓的
  • ConsumereConfig:定义了用来调用服务提供者的RestTemplatebean
@Configuration
public class ConsumerConfig {
    @LoadBalanced //使用负载均衡
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
  • ConsumerController:控制器,本身没有任何值得讲的点,注意一下通过RestTemplate调用zed-service-user服务的方法:
@RestController
@RequestMapping("/consumer")
@AllArgsConstructor
public class ConsumerController {
    private RestTemplate restTemplate;

    @GetMapping("/user/{id}")
    public Result queryById(@PathVariable("id") Long id){
        return restTemplate.getForEntity("http://ZED-SERVICE-USER/user/{id}", Result.class, id).getBody();
    }
}

其中的

restTemplate.getForEntity(
				"http://ZED-SERVICE-USER/user/{id}",  
				//ZED-SERVICE-USER为user服务在eureka上注册名的大写,可以通过访问eureka的/apps API查看(http://localhost:8762/eureka/apps)
				Result.class, //返回类型的class
				id) //参数
			.getBody();

4.3.4 主程序

@EnableEurekaClient
@SpringBootApplication
public class ZedConsumerServiceApplication {
    public static void main(String args[]){
        SpringApplication.run(ZedConsumerServiceApplication.class, args);
    }
}

平淡无奇。

5、数据库

5.1 导入

导入db目录下的zed.sql文件。数据库端口为默认的3306,用户名密码均为root。当然这都是可以随便更改的,只要项目中配置文件对应就好了。

5.2 表

5.2.1 zed_user 用户信息表

在这里插入图片描述
其他的暂时不需要

6、运行

6.1 运行

  1. 运行eureka服务器
  2. 运行服务提供者zed-service-user
  3. 修改一下zed-service-userbootstrap.xml文件中定义的server.port端口号,再次运行一个zed-service-user实例。再运行100个也无所谓,运行两个就能看到负载均衡的效果了。
  4. 运行服务消费者zed-service-consumer

6.2 eureka界面

在这里插入图片描述
可以看到ZED-SERVICE-USER启动了两个实例。

7、测试

7.1 直接访问ZED-SERVICE-USER

在这里插入图片描述

7.2 通过ZED-SERVICE-CONSUMER多次访问

  1. 第一次
    在这里插入图片描述
  2. 第二次
    在这里插入图片描述

7.3 结论

多次访问可以看到,消费者是轮流调用生产者的不同实例的,做到了负载均衡。

8、欢迎提问


转载请注明来源
https://blog.csdn.net/SingingFisher/article/details/88660952


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值