Springcloud 介绍 和 Eureka的使用

Springcloud 介绍 和 Eureka的使用

第一章 Springcloud 基础理论

视频来源:Spring-cloud 1 1-27

什么是微服务?

微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间相互协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通行机制相互协作(通常是基于HTTP协议的RestFul API)。每个服务都围绕着具体本业务进行构建,并且能够被独立部署到生成环境、类生产环境等。另外,应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。

拆分来理解就是:

  • 微服务是一种架构风格
  • 每个服务运行在自己独立的机器中,也就是可以单独的部署和升级
  • 服务之间使用轻量级的HTTP交互
  • 每个服务都提供各自的业务功能
  • 可以由全自动部署机制独立部署
  • 每个服务可以使用不同的语言、不同的存储过程

主题词01:现代数字化生活-落地维度

  • 手机
  • PC
  • 智能家居
  • VR
  • 笔记本

主题词02:分布式微服务架构-落地维度

images

  • 服务调用
  • 服务降级
  • 服务注册与发现
  • 服务熔断
  • 负载均衡
  • 服务消息队列
  • 服务网关
  • 配置中心管理
  • 自动化构建部署
  • 服务监控
  • 全链路追踪
  • 服务定时任务
  • 调度操作

SpringCloud介绍

Spring Cloud是什么? 什么是微服务架构

images

springcloud官方文档(Hoxton SR5):https://cloud.spring.io/spring-cloud-static/Hoxton.SR5/reference/htmlsingle/
springcloud中文文档:https://www.springcloud.cc/
springcloud中国社区文档:http://docs.springcloud.cn/
https://www.bookstack.cn/read/spring-cloud-docs/docs-index.md

SpringCloud=分布式微服务架构的一站式解决方案,是多种微服务架构落地的技术框架的集合体,俗称微服务全家桶

SpringCloud 包含的技术组件?

images

SpringCloud版本选择

Spring Boot 源码地址

Spring Cloud 源码地址

Spring Boot 与 Spring Cloud 兼容性查看

2022年3月27日20:05:45查看版本对应的选择
images

接下来开发用到的组件版本

  • Cloud-Hoxton.SR1
  • Boot- 2.2.2.RELEASE
  • Cloud Alibaba-2.1.0.RELEASE
  • JAVA -JAVA8
  • Maven -3.5及以上
  • MySQL -5.7及以上

Cloud组件停更说明

停更引发的升级惨案

  • 停更不停
  • 被动修复bugs
  • 不再接受合并请求
  • 不再发布新版本

Cloud升级

服务注册中心

  • × Eureke
  • √ Zookeeper
  • √ Consul
  • √ Nacos

服务调用

  • √ Ribbon
  • √ LoadBalancer
  • × Fegin
  • √ OpenFegin

服务降级

  • × HyStrix
  • √ resilience4j
  • √ sentinel

服务网关

  • × Zuul
  • !Zuul2
  • √ gateway

服务配置

  • × Config
  • √ Nacos

服务总线

  • × Bus
  • √ Nacos

父工程项目搭建

约定 -> 配置 -> 编码

创建微服务cloud整体聚合父工程Project ,有8个关键步骤:

1.New Project-maven工程

images

2.聚合功总工程名称
images

3.Maven版本(使用3.5.2)
images

images

4.工程名称

5.字符编码 - Setting -File encoding(选择UTF-8)

images

6.注解生效激活 -Setting-Annotation Processors

images

7.Java编译版本选8

images

8.File Type过滤 -Setting -File Type

images

父工程pom文件

<!--统一管理jar包版本-->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <junit.version>4.12</junit.version>
    <lombok.version>1.18.10</lombok.version>
    <log4j.version>1.2.17</log4j.version>
    <mysql.version>8.0.18</mysql.version>
    <druid.version>1.1.20</druid.version>
    <mybatis.spring.boot.version>1.3.2</mybatis.spring.boot.version>
  </properties>

  <!--子模块继承之后,提供作用:锁定版本+子module不用写groupId和version-->
  <dependencyManagement>
    <dependencies>
      <!--spring boot 2.2.2-->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.2.2.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--spring cloud Hoxton.SR1-->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Hoxton.SR1</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--spring cloud alibaba-->
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2.1.0.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--mysql-->
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
        <scope>runtime</scope>
      </dependency>
      <!-- druid-->
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>${druid.version}</version>
      </dependency>

      <!--mybatis-->
      <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis.spring.boot.version}</version>
      </dependency>
      <!--junit-->
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
      </dependency>
      <!--log4j-->
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
      </dependency>
    </dependencies>

  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <fork>true</fork>
          <addResources>true</addResources>
        </configuration>
      </plugin>
    </plugins>
  </build>

复习DependencyManager和Dependencied

Maven使用dependencyManagement元素来提供了一种管理依赖版本号的方式。

通常会在一个项目的最顶层的父POM中看到DependencyManager元素

使用pom.xml中的dependencyManagement元素能让所有的子项目中引用个依赖而不用显示的列出版本量。

Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用这个dependencyManagement元素中的指定的版本号。

然后子元素可以添加父引入的依赖可以不携带版本号方便统一管理。

  • dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖
  • 如果子项目中的声明依赖,是不会从父项目中继承下来的;只有子项目中写了该依赖,并且没有指定具体版本,才会从父工程中继承该项,并且version和scope都读取自父pom。
  • 如果子项目指定了对应的版本号,那么会使用子项目中指定的jar版本

IDEA右侧有maven插件有Toggle Skip Test’s Mode按钮 ,这样maven可以跳过单元测试

images


父工程创建完成执行 mvn:install 将会把父工程发布到仓库方面子工程继承。

Rest微服务工程构建

最开始的订单模块
images

模块构建:

  1. 建module
  2. 改pom
  3. 写yml
  4. 主启动
  5. 业务类
  6. 测试

1. 微服务提供者支付module模块cloud-provider-payment8001

1. 新建模块

images

images

images

2. 在pom中添加依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.20</version>
            <!--子工程写了版本号,就使用子工程的版本号,如果没写版本,找父工程中规定的版本号-->
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3. 写yml
#微服务建议一定要写服务端口号和微服务名称
server:
  #端口号
  port: 8001

spring:
  application:
    #微服务名称
    name: cloud-payment-service
  #数据库配置
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    #mysql5.x的没有cj
    driver-class-name: com.mysql.cj.jdbc.Driver
    #记得先创建数据库
    url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 200088lx

#mybatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.zero.entity  #所有Entity别名类所在包
4. 主启动

在java包下创建主启动类com.angenin.springcloud.PaymentMain8001

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

5.业务类
  1. 建标SQL
CREATE TABLE `payment`(
	`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
	`serial` VARCHAR(200) DEFAULT '',
	PRIMARY KEY(`id`)
)ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT INTO payment(`serial`)VALUES("张三");

2.entity

在对应自己创建的entity包下面创建出对应实体

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}

在entity包下新建CommonResult(json封装体 ,传给前端的)

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{
    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code, String message){
        this(code, message, null);
    }
}

3.dao

在对应包下新建Dao.PaymentDao接口

@Mapper
//@Repository不用Spring的
public interface PaymentDao
{
    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") Long id);
}

4.mapper

在resource目录下新建mapper目录,然后新建PaymentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.zero.mapper.PaymentDao">

    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial)  values(#{serial});
    </insert>

    <resultMap id="BaseResultMap" type="com.zero.entity.Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <id column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>

    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
        select * from payment where id=#{id};
    </select>

</mapper>

5.service

在springcloud包下新建service.PaymentService接口

public interface PaymentService
{
    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") Long id);
}

在service包下新建impl.PaymentServiceImpl实现下

@Service
public class PaymentServiceImpl implements PaymentService
{
    @Resource
    private PaymentDao paymentDao;

    @Override
    public int create(Payment payment)
    {
        return paymentDao.create(payment);
    }

    @Override
    public Payment getPaymentById(Long id)
    {
        return paymentDao.getPaymentById(id);
    }
}
  1. controller

在springcloud包下新建controller.PaymentController

@RestController
@Slf4j
public class PaymentController{
    @Resource
    private PaymentService paymentService;

    @PostMapping(value = "/payment/create")
    public CommonResult create(Payment payment)
    {
        int result = paymentService.create(payment);
        log.info("*****插入结果:"+result);

        if(result > 0)
        {
            return new CommonResult(200,"插入数据库成功",result);
        }else{
            return new CommonResult(444,"插入数据库失败",null);
        }
    }

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
    {
        Payment payment = paymentService.getPaymentById(id);

        if(payment != null)
        {
            return new CommonResult(200,"查询成功",payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: "+id,id);
        }
    }
}
6.测试
  1. 浏览器-http://localhost:8001/payment/get/1
  2. Postman - http://localhost:8001/payment/create?serial=lun2 (post接口实现postman测试)

2.热部署Devtools (只能在开发阶段使用)

  1. 在cloud-provider-payment8001项目中添加热部署依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

2.添加一个插件到父类工程的pom.xml里

<build>
    <!--
	<finalName>你的工程名</finalName>(单一工程时添加)
    -->
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <fork>true</fork>
                <addResources>true</addResources>
            </configuration>
        </plugin>
    </plugins>
</build>

  1. Enabling automatic build

File -> Settings(New Project Settings->Settings for New Projects) ->Complier

images

  1. 热注册开启

组合键 Shift+Ctrl+Alt+/ ,选中Registry

  • compiler.automake.allow.when.app.running
  • actionSystem.assertFocusAccessFromEdt

images

images

images

  1. 重启IDEA

3.微服务消费订单module模块cloud-consumer-order81

1.新建模块
cloud-consumer-order81
2.改pom:

往pom中添加:

<?xml version="1.0" encoding="UTF-8"?>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

3.写yml

在resouce目录下新建application.yml

#访问一个网站时,默认是80端口,给用户80端口,用户就可以不用加端口直接访问页面
server:
  port: 81  
4.主启动
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @Auther: lixiang
 * @Date: 2022/03/30/22:57
 * @Description:
 */
@SpringBootApplication
public class OrderMain81
{
    public static void main( String[] args ){
        SpringApplication.run(OrderMain81.class, args);
    }
}
5.业务类
  1. 编写实体类:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{
    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code, String message){
        this(code, message, null);
    }
}

  1. 在包下面新建config.ApplicationContextConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}

  1. 在包下新建controller.OrderController
import com.lun.springcloud.entities.CommonResult;
import com.lun.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Slf4j
@RestController
public class OrderController {

    public static final String PAYMENT_URL = "http://localhost:8001";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/create")
    public CommonResult<Payment> create(Payment payment){

        return restTemplate.postForObject(PAYMENT_URL+"/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, CommonResult.class);
    }
}
6.测试

运行cloud-consumer-order80与cloud-provider-payment8001两工程

浏览器 - http://localhost:81/consumer/payment/get/1

7.RestTemplate介绍

RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集

官网地址

使用:

使用restTemplate访问restful接口非常的简单粗暴无脑。

(url, requestMap, ResponseBean.class)这三个参数分别代表。

REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。

3. 遗留问题

浏览器 - http://localhost:81/consumer/payment/create?serial=lun3

虽然,返回成功,但是观测数据库中,并没有创建serial为lun3的行。

解决方法:在cloud-provider-payment8001工程的PaymentController中添加@RequestBody注解。

public class PaymentController
{

    @PostMapping(value = "/payment/create")
    public CommonResult create(@RequestBody/*添加到这里*/ Payment payment){
		...
    }
}
  • 开启Run DashBoard

打开工程路径下的.idea文件夹的workspace.xml

在中修改或添加以下代码:

<option name="configurationTypes">
	<set>
		<option value="SpringBootApplicationConfigurationType"/>
    </set>
</option>

4. 工程重构

观察cloud-consumer-order81与cloud-provider-payment8001两工程有重复代码(entity)(公共代码),重构。

1.新建module

cloud-api-commons

2.POM
   <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>
3. entity

将cloud-consumer-order81与cloud-provider-payment8001两工程的公有entities包移至cloud-api-commons工程下。

4. maven重新打包
  • maven clean、install

  • cloud-api-commons工程,以供给cloud-consumer-order81与cloud-provider-payment8001两工程调用。

5. 引入cloud-api-commons包
  • 将cloud-consumer-order81与cloud-provider-payment8001两工程的公有entities包移除
  • 引入cloud-api-commons依赖
        <dependency>
            <groupId>com.zero</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
6.测试

Eureka服务注册与发现

Eureka 基础知识

服务治理

什么是服务治理

Spring Cloud封装了Netflix公司开发的Eureka模块来实现服务治理

在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

服务注册

什么是服务注册与发现

Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。

在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以别名的方式去注册中心上获取到实际的通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))

images

Eureka两组件

Eureka包含两个组件:Eureka Server和Eureka Client

Eureka Server提供服务注册服务

各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

EurekaClient通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)

单机Eureka构建步骤

IDEA生成EurekaServer端服务注册中心

1.建module

cloud-eureka-server7001的Maven工程

2.改pom

server端依赖对比:

images

dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.zero</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
3.写yml

在resource目录下新建application.yml文件

server:
  port: 7001

eureka:
  instance:
    hostname: locathost #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

4.主启动
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

测试运行EurekaMain7001,浏览器输入http://localhost:7001/回车,会查看到Spring Eureka服务主页。

将EurekaClient端8001注册进EurekaServer成为服务提供者provider

1. 修改cloud-provider-payment8001
2. 改pom

添加spring-cloud-starter-netflix-eureka-client依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.写yml
eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka
4.主启动
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient//<-----添加该注解
public class PaymentMain001 {

    public static void main(String[] args) {
        SpringApplication.run(PaymentMain001.class, args);
    }
}

5.测试
  • 启动cloud-provider-payment8001和cloud-eureka-server7001工程。
  • 浏览器输入 - http://localhost:7001/ 主页内的Instances currently registered with Eureka会显示cloud-provider-payment8001的配置文件application.yml设置的应用名cloud-payment-service
6. 自我保护机制

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARELESSER THAN THRESHOLD AND HENCFT ARE NOT BEING EXPIRED JUST TO BE SAFE.

紧急情况!EUREKA可能错误地声称实例在没有启动的情况下启动了。续订小于阈值,因此实例不会为了安全而过期。

将EurekaClient端81注册进EurekaServer成为服务消费者consumer

EurekaClient端cloud-consumer-order81将注册进EurekaServer成为服务消费者consumer,类似来上课消费的同学

1.引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.在yml文件中添加
server:
  port: 81

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka
3.主启动
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient//<--- 添加该标签
public class OrderMain81
{
    public static void main( String[] args ){
        SpringApplication.run(OrderMain80.class, args);
    }
}

4.启动项目 刷新页面
  • 启动cloud-provider-payment8001、cloud-eureka-server7001和cloud-consumer-order80这三工程。
  • 浏览器输入 http://localhost:7001 , 在主页的Instances currently registered with Eureka将会看到cloud-provider-payment8001、cloud-consumer-order81两个工程名。

Eureka集群原理说明

images

搭建Eureka注册集群,实现负载均衡+故障容错。

构建eurekaServer集群环境

  1. 参照cloud-eureka-server7001新建cloud-eureka-server7002和cloud-eureka-server7003。
  2. 修改hosts文件(路径在C:\Windows\System32\drivers\etc路径中找到hosts文件。)
127.0.0.1       eureka7001.com
127.0.0.1       eureka7002.com
127.0.0.1		eureka7003.com
  1. 修改7001的yml文件
eureka:
  instance:
    hostname: eureka7001.com  #eureka服务端的实例名称
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
#      单机
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      #集群版  相互注册,相互守望
      defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/ 
      
#    defaultZone是固定写法,如果想自定义,需要按以下写法才行:
#    region: eureka-server
#    availability-zones:
#      eureka-server: server1,server2
#    service-url:
#      server1: http://eureka7002.com:7002/eureka/
#      server2: http://eureka7003.com:7003/eureka/

  1. 修改7002的yml文件
eureka:
  instance:
    hostname: eureka7002.com  #eureka服务端的实例名称
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      #集群版  相互注册,相互守望
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7003.com:7003/eureka/ #相互注册,相互守望

  1. 修改7003的yml文件
eureka:
  instance:
    hostname: eureka7003.com  #eureka服务端的实例名称
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      #集群版  相互注册,相互守望
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/ #相互注册,相互守望
  1. 分别启动该cloud-eureka-server7001, cloud-eureka-server7002和cloud-eureka-server7003

将将支付服务8001和订单服务81微服务发布到集群配置中

把两个项目的yml文件中的defaultZone改为:

#集群版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka

启动5个项目进行测试:(先启动集群,再启动8001,最后启动81)

支付微服务集群配置

  1. 按照8001新建8002。(除了要yml文件中需要改端口号和主配置类,其他直接复制8001的,yml文件中的应用名不需要改,因为是集群,所以应用名需要一致)
  2. 分别在所有的提供者的PaymentController中加入:(这个@Value是spring的注解)
@RestController
@Slf4j
public class PaymentController{
    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @PostMapping(value = "/payment/create")
    public CommonResult create(@RequestBody Payment payment)
    {
        int result = paymentService.create(payment);
        log.info("*****插入结果:"+result);

        if(result > 0)
        {
            return new CommonResult(200,"插入数据库成功 serverPort:" + serverPort,result);
        }else{
            return new CommonResult(444,"插入数据库失败",null);
        }
    }

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
    {
        Payment payment = paymentService.getPaymentById(id);

        if(payment != null)
        {
            return new CommonResult(200,"查询成功 serverPort:" + serverPort,payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: "+id);
        }
    }
}
  1. 修改消费者OrderController,把写死的地址改为服务器名称
   	public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
  1. 然后在消费者的ApplicationContextConfig里的restTemplate方法上加上@LoadBalanced,开启RestTemplate开启负载均衡功能。
@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}
  1. 测试

先启动EurekaServer , 7001/70025服务

在启动服务提供者Provider , 8001/8002服务

浏览器输入 - http://localhost:81/consumer/payment/get/1

结果:负载均衡效果达到,8001/8002端口交替出现。

images

images

actuator微服务信息完善

主机名称:服务名称修改(也就是将IP地址,换成可读性比较高的名字)
访问信息有IP信息提示(鼠标移至服务名称下面 , 会显示对应的IP地址显示)

修改cloud-provider-payment8001,cloud-provider-payment8002,consumer81 名称

#######8001#######
# client:	
#	 ...	instance要和client对齐
  instance:
    instance-id: payment8001
    prefer-ip-address: true   #访问路径可以显示ip地址

#######8002#######
  instance:
    instance-id: payment8002   #修改显示的主机名
    prefer-ip-address: true   #访问路径可以显示ip地址
    
#######80#######
  instance:
    instance-id: consumer80   #修改显示的主机名
    prefer-ip-address: true   #访问路径可以显示ip地址

images

服务发现Discovery

对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息。

修改提供者集群的Controller

  1. 在主配置类上加上@EnableDiscoveryClient注解,启用发现客户端。
  2. 在两个提供者的PaymentController中加入:
修改cloud-provider-payment8001的主启动
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class, args);
    }
}
修改cloud-provider-payment8001的Controller

  @Resource
    private DiscoveryClient discoveryClient;

    @GetMapping("/payment/discovery")
    public Object discovery(){
        //获取服务列表的信息
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            log.info("*******element:" + element);
        }

        //获取CLOUD-PAYMENT-SERVICE服务的所有具体实例
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance instance : instances) {
            //getServiceId服务器id getHost主机名称 getPort端口号  getUri地址
            log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());
        }

        return this.discoveryClient;
    }
测试
  1. 重启cloud-provider-payment8001服务
  2. 浏览器输入:http://localhost:8001/payment/discovery

浏览器输出:

{"services":["cloud-payment-service","cloud-order-service"],"order":0}

后台输出:

2022-04-03 22:18:50.754  INFO 2820 --- [nio-8001-exec-1] com.zero.controller.PaymentController    : *******element:cloud-payment-service
2022-04-03 22:18:50.754  INFO 2820 --- [nio-8001-exec-1] com.zero.controller.PaymentController    : *******element:cloud-order-service
2022-04-03 22:18:50.758  INFO 2820 --- [nio-8001-exec-1] com.zero.controller.PaymentController    : CLOUD-PAYMENT-SERVICE	192.168.56.1	8002	http://192.168.56.1:8002

Eureka自我保护理论知识

概述

保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,EurekaServer将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注册任何微服务。

如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:

images

导致原因

某时刻某一个微服务不可用了,Eureka不会立即清除,依旧会对该服务的信息进行保存。

属于CAP里面的AP分支。

为了EurekaClient端可以正常运行,防止了网络通信不良的情况下,server端不会立即把Client端给剔除掉。

什么是自我保护机制

默认情况下如果在 Server在一定时间内没有介绍到某个Client端没有接受到某个微服务实例的心跳,server端将会注销该实例(默认90秒)。但是当网络分区故障发生 ,client与server之间正常无法通行,本身client端是很健康的本不应该立马去注销这个微服务,Eureka通过自我保护模式来解决这个问题。

自我保护机制:默认情况下client定时向server端发送心跳包(默认间隔90秒),便会直接从服务注册列表中剔除该服务,但是在短时间内丢失了大量的服务实例心跳,这时候server会开启自我保护机制,不会剔除该服务。

怎么禁止自我保护

在EurekaServer端7001处设置关闭自我保护机制

默认配置是开启的

#  client
#  	...		server与client对齐
  server:
    #关闭自我保护,默认为true
    enable-self-preservation: false
    #心跳的间隔时间,单位毫秒
    eviction-interval-timer-in-ms: 2000

关闭效果:

THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

在EurekaClient端8001

cloud-provider-payment8001的yml文件:

  instance:
    instance-id: payment8001
    prefer-ip-address: true   #访问路径可以显示ip地址
    #Eureka客户端向服务端发送心跳的时间间隔,单位秒(默认30秒)
    lease-renewal-interval-in-seconds: 1
    #Eureka服务端在收到最后一次心跳后等待的时间上限,单位秒(默认90秒),超时剔除服务
    lease-expiration-duration-in-seconds: 2

测试:
* 7001和8001都配置完成
* 先启动7001再启动8001

结果:先关闭8001,马上被删除了

images

images

Eureka停更说明

Eureka 2.0 (Discontinued)
The existing open source work on eureka 2.0 is discontinued. The code base and artifacts that were released as part of the existing repository of work on the 2.x branch is considered use at your own risk.
Eureka 1.x is a core part of Netflix’s service discovery system and is still an active project.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值