SpringCloud学习笔记(三)搭建EurekaClient

一、创建服务提供者 - 支付模块

1、初始化数据库

新建数据库 db2020

2、创建子module项目 – 服务提供者

cloud-provider-payment8001

3、修改pom文件
<?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>spring-cloud-demo</artifactId>
        <groupId>com.cloud.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8001</artifactId>

    <dependencies>
        <dependency>
            <!-- 引用自己定义的api通用包 -->
            <groupId>com.cloud.demo</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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-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>
        </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>

</project>

此处的eureka的maven信息为

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
4、添加application.yml
server:
  port: 8001

spring:
  application:
    name: payment-provider-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource      #当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver        #mysql驱动包
    url: jdbc:mysql://localhost:3306/db2020?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding-utr-8&useSSL=false
    username: root
    password: south755

eureka:
  user:
    name: admin
    password: 123456
  client:
    #表示是否将自己注册进EurekaServer默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:
      # eureka 注册地址
      defaultZone: http://${eureka.user.name}:${eureka.user.password}@localhost:7001/eureka #单机版

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.demo.common.entity       #所有Entity别名类所在包
5、添加启动程序
package com.demo.payment;

@SpringBootApplication
//前往eureka 注册中心注册服务
@EnableEurekaClient
public class PaymentMain8001 {

    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class, args);
        System.out.println("PaymentMain8001 已启动");
    }
}
6、编写业务代码
创建数据表
编写mapper层
编写Service层
编写Controller层

常规crud操作,推荐个sql生成业务代码的网站,总有会用到的时候
在线Sql代码生成

  1. 数据库建表,与实体类
CREATE TABLE `payment` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `serial` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
@Data
public class Payment implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * id
     */
    private Long id;

    /**
     * serial
     */
    private String serial;

    public Payment() {
    }

}
  1. mapper层接口与xml
@Mapper
public interface PaymentDao {

    /**
     * [新增]
     * @author PeiKangLe
     * @date 2020/05/14
     **/
    int insert(Payment payment);

    /**
     * [刪除]
     * @author PeiKangLe
     * @date 2020/05/14
     **/
    int delete(Long id);

    /**
     * [更新]
     * @author PeiKangLe
     * @date 2020/05/14
     **/
    int update(Payment payment);

    /**
     * [查询] 根据主键 id 查询
     * @author PeiKangLe
     * @date 2020/05/14
     **/
    Payment load(Long id);

}
<?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.demo.payment.dao.PaymentDao">

    <resultMap id="BaseResultMap" type="com.demo.common.entity.Payment" >
        <result column="id" property="id" />
        <result column="serial" property="serial" />
    </resultMap>

    <sql id="Base_Column_List">
                id,
                serial
    </sql>

    <insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id" parameterType="Payment">
        INSERT INTO payment
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test ='null != serial'>
                serial
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test ='null != serial'>
                #{serial}
            </if>
        </trim>
    </insert>

    <delete id="delete" >
        DELETE FROM payment
        WHERE id = #{id}
    </delete>

    <update id="update" parameterType="Payment">
        UPDATE payment
        <set>
            <if test ='null != serial'>serial = #{serial}</if>
        </set>
        WHERE id = #{id}
    </update>

    <select id="load" resultMap="BaseResultMap">
        SELECT <include refid="Base_Column_List" />
        FROM payment
        WHERE id = #{id}
    </select>

</mapper>
  1. Service层
public interface PaymentService {

    /**
     * 新增
     */
    public int insert(Payment payment);

    /**
     * 删除
     */
    public int delete(Long id);

    /**
     * 更新
     */
    public int update(Payment payment);

    /**
     * 根据主键 id 查询
     */
    public Payment load(Long id);

}
@Service
public class PaymentServiceImpl implements PaymentService {

    @Resource
    private PaymentDao paymentDao;

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

    @Override
    public int delete(Long id) {
        return paymentDao.delete(id);
    }

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

    @Override
    public Payment load(Long id) {
        return paymentDao.load(id);
    }
}

  1. Controller层

为返回结果方便,我们先在Common模块里添加统一返回对象

public class ResultModel extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;

    /**
     * 状态码
     */
    public static final String CODE_TAG = "code";

    /**
     * 返回内容
     */
    public static final String MSG_TAG = "msg";

    /**
     * 数据对象
     */
    public static final String DATA_TAG = "data";

    /**
     * 状态类型
     */
    public enum Type {
        /**
         * 成功
         */
        SUCCESS(0),
        /**
         * 警告
         */
        WARN(301),
        /**
         * 错误
         */
        ERROR(500);
        private final int value;

        Type(int value) {
            this.value = value;
        }

        public int value() {
            return this.value;
        }
    }

    public ResultModel() {
    }

    /**
     * 初始化一个新创建的 ResultModel 对象
     *
     * @param type 状态类型
     * @param msg  返回内容
     */
    public ResultModel(Type type, String msg) {
        super.put(CODE_TAG, type.value);
        super.put(MSG_TAG, msg);
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param type 状态类型
     * @param msg  返回内容
     * @param data 数据对象
     */
    public ResultModel(Type type, String msg, Object data) {
        super.put(CODE_TAG, type.value);
        super.put(MSG_TAG, msg);
        if (!Objects.isNull(data)) {
            super.put(DATA_TAG, data);
        }
    }

    /**
     * 返回成功消息
     *
     * @return 成功消息
     */
    public static ResultModel success() {
        return ResultModel.success("操作成功");
    }

    /**
     * 返回成功数据
     *
     * @return 成功消息
     */
    public static ResultModel success(Object data) {
        return ResultModel.success("操作成功", data);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @return 成功消息
     */
    public static ResultModel success(String msg) {
        return ResultModel.success(msg, null);
    }

    /**
     * 返回成功消息
     *
     * @param msg  返回内容
     * @param data 数据对象
     * @return 成功消息
     */
    public static ResultModel success(String msg, Object data) {
        return new ResultModel(Type.SUCCESS, msg, data);
    }

    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static ResultModel warn(String msg) {
        return ResultModel.warn(msg, null);
    }

    /**
     * 返回警告消息
     *
     * @param msg  返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static ResultModel warn(String msg, Object data) {
        return new ResultModel(Type.WARN, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @return
     */
    public static ResultModel error() {
        return ResultModel.error("操作失败");
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static ResultModel error(String msg) {
        return ResultModel.error(msg, null);
    }


    /**
     * 返回错误消息
     *
     * @param msg  返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static ResultModel error(String msg, Object data) {
        return new ResultModel(Type.ERROR, msg, data);
    }

}

再来添加Controller

@Slf4j
@RestController
@RequestMapping(value = "/Payment")
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    /**
     * [新增]
     *
     * @author PeiKangLe
     * @date 2020/05/14
     **/
    @PostMapping("/insert")
    public ResultModel insert(@RequestBody Payment payment) {
        int insert = paymentService.insert(payment);
        log.info("添加信息:{},添加结果:{}", payment.toString(), insert);
        return insert > 0 ? ResultModel.success("添加成功") : ResultModel.error("添加失败");
    }

    /**
     * [刪除]
     *
     * @author PeiKangLe
     * @date 2020/05/14
     **/
    @DeleteMapping("/{id}")
    public ResultModel delete(@PathVariable("id") Long id) {
        int delete = paymentService.delete(id);
        log.info("删除信息:{},删除结果:{}", id, delete);
        return delete > 0 ? ResultModel.success("删除成功") : ResultModel.error("删除失败");
    }

    /**
     * [更新]
     *
     * @author PeiKangLe
     * @date 2020/05/14
     **/
    @PutMapping("/update")
    public ResultModel update(Payment payment) {
        int update = paymentService.update(payment);
        log.info("修改信息:{},修改结果:{}", payment.toString(), update);
        return update > 0 ? ResultModel.success("修改成功") : ResultModel.error("修改失败");
    }

    /**
     * [查询] 根据主键 id 查询
     *
     * @author PeiKangLe
     * @date 2020/05/14
     **/
    @GetMapping("/{id}")
    public ResultModel load(@PathVariable("id") Long id) {
        return ResultModel.success("查询成功", paymentService.load(id));
    }

}
  1. 运行项目,进行测试
    首先启动我的7001 eurekaServer
    再运行我们的项目
    启动完成后查看http://localhost:7001/
    在这里插入图片描述
    可以看到我们的服务已经启动成功了,并且已经成功注册到eurekaServer。
    红色字体是Eureka的警告,暂时不去管它。

插入数据测试
在这里插入图片描述
功能测试也是正常。

二、创建服务消费者 – 订单模块

1、创建子module项目 – 服务消费者

cloud-consumer-order80

2、修改pom文件
<?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>spring-cloud-demo</artifactId>
        <groupId>com.cloud.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-order80</artifactId>

    <dependencies>
        <dependency>
            <!-- 引用自己定义的api通用包 -->
            <groupId>com.cloud.demo</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <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.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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、添加application.yml
server:
  port: 80

spring:
  application:
    name: order-consumer-service

eureka:
  user:
    name: admin
    password: 123456
  client:
    #表示是否将自己注册进EurekaServer默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:
      # eureka 注册地址
      defaultZone: http://${eureka.user.name}:${eureka.user.password}@localhost:7001/eureka #单机版
4、添加启动类

同样用@EnableEurekaClient将我们的服务注册到EurekaServer

@SpringBootApplication
@EnableEurekaClient
public class OrderMain80 {

    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class,args);
        System.out.println("Oder80 启动成功");
    }
}
5、添加RestTemplate,调用我们的支付服务
@Configuration
public class ApplicationContextConfig {
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
6、Controller层调用我们上面的支付服务

说明:Payment通用的,为了方便,我就放到common模块里去了

@Slf4j
@RestController
@RequestMapping("/Order")
public class OrderController {

    @Resource
    private RestTemplate restTemplate;
    private static final String paymentUrl = "http://localhost:8001/Payment/";

    @GetMapping("/{id}")
    public ResultModel getPayment(@PathVariable("id") Long id){
        return restTemplate.getForObject(paymentUrl+id, ResultModel.class );
    }

    @PostMapping("/create")
    public ResultModel createPayment(Payment payment){
        return restTemplate.postForObject(paymentUrl+"insert",payment, ResultModel.class );
    }
    
}
7、启动项目,测试

在这里插入图片描述
可以看到,我们两个服务都已经成功注册到EurekaServer里了

测试功能
在这里插入图片描述
在这里插入图片描述
可以看到,服务注册成功,功能测试正常。

三、补充

eureka自我保护模式

自我保护模式

在这里插入图片描述
之前遇见的上面的提示,就是告诉我们Eureka进入了保护模式。
默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,这就可能变得非常危险了----因为微服务本身是健康的,此时本不应该注销这个微服务。

Eureka Server通过“自我保护模式”来解决这个问题----当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

自我保护模式是一种对网络异常的安全保护措施。使用自我保护模式,而已让Eureka集群更加的健壮、稳定。

简单来说,就是有些时刻微服务不可用了
Eureka不会马上清理它,会对该微服务的信息进行保护。
关闭服务保护模式
	eureka:
	   server:
	     # 是否开启自我保护模式,默认是true
	     enable-self-preservation: false
	     # 扫描失效服务的时间间隔。单位 毫秒。 默认值 60 * 1000
	     eviction-interval-timer-in-ms: 1000
优雅关闭服务(优雅停服)

在Spring Cloud中,可以通过HTTP请求的方式,通知Eureka Client优雅停服,这个请求一旦发送到Eureka Client,那么Eureka Client会发送一个shutdown请求到Eureka Server,Eureka Server接收到这个shutdown请求后,会在服务列表中标记这个服务的状态为down,同时Eureka Client应用自动关闭。这个过程就是优雅停服。

如果使用了优雅停服,则不需要再关闭Eureka Server的服务保护模式。

  1. 在Eureka Client中增加新的依赖actuator
	 <dependency>
	     <groupId>org.springframework.boot</groupId>
	     <artifactId>spring-boot-starter-actuator</artifactId>
	 </dependency>
  1. Eureka Client默认不开启优雅停服功能,需要在全局配置文件中新增如下内容
	 # 开启应用关闭端口
	 management.endpoint.shutdown.enabled=true
	 # 暴露shutdown的web端口
  1. 发起shutdown请求:

必须通过POST请求向Eureka Client发起一个shutdown请求。请求路径为:http://ip:port/shutdown。可以通过任意技术实现,如:HTTPClient、form表单,AJAX等。

建议使用优雅停服方式来关闭Application Service/Application Client服务。

Eureka 的常用配置项

  1. 通用配置
spring.application.name=xxxxxxxxxxxxx :应用名称配置,将会出现在 Eureka 注册中心 Application 列
server.port=8701 :应用端口,默认值为 8761
eureka.instance.hostname= server1 :服务注册中心应用实例主机名
eureka.instance.ip-address=127.0.0.1 :应用实例ip
eureka.instance.prefer-ip-address=false :客户端向注册中心注册时,相较于 hostname 是否有限使用 ip。在服务中心注册后,鼠标放到服务的 Status 列的链接上,无需点击,左下角能看出配置的变化。
eureka.instance.environment=dev :该实例的环境配置
eureka.client.register-with-eureka=false :是否将自己注册到 Eureka 注册中心。单机情况下的 Eureka Server 不需要注册,集群的 Eureka Server 以及 Eureka Client 需要注册。默认值 true
eureka.client.fetch-registry=false :是否需要从注册中心检索获取服务的注册信息。单机情况下的 Eureka Server 不需要获取。集群的 Eureka Server 以及 Eureka Client 需要获取。默认值 true
eureka.client.service-url.defaultZone= http://${spring.security.user.name}:${spring.security.user.password}@server1:8081/eureka/ :Eureka 服务的地址信息,中间的占位符为安全认证开启时使用,如果 Eureka Server 为集群状态,则逗号分隔,依次书写即可。
  1. Eureka Server 配置
eureka.server.enable-self-preservation = false :是否开启自我保护模式,eureka server默认在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,让这些实例不会过期,但是在保护期内如果服务刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败。默认 true
eureka.server.eviction-interval-timer-in-ms=10000 :扫描失效服务的时间间隔。单位 毫秒。 默认值 60 * 1000
security.basic.enabled=true :开启 Eureka 安全认证
spring.security.user.name=root :安全认证用户名
spring.security.user.password=123456 :安全认证密码
  1. Eureka Client 配置
eureka.client.registry-fetch-interval-seconds=30 :客户端获取服务注册信息时间间隔,单位 秒。默认 30
eureka.instance.appname=eureka-client :服务名,默认取 spring.application.name 配置值,如果没有则为 unknown
eureka.instance.lease-expiration-duration-in-seconds=90 :服务的失效时间,失效的服务将被注册中心删除。时间间隔为最后一次注册中心接收到的心跳时间。单位 秒,默认 90
eureka.instance.lease-renewal-interval-in-seconds=30 :应用实例给注册中心发送心跳的间隔时间,用于表明该服务实例可用。单位 秒。默认30  
eureka.client.eureka-server-connect-timeout-seconds=5 :client 连接 Eureka 注册中心的超时时间,单位 秒,默认 5
eureka.client.eureka-server-read-timeout-seconds=8 :client 对 Eureka 服务器读取信息的超时时间,单位 秒,默认 8
eureka.client.eureka-connection-idle-timeout-seconds=30 :client 连接 Eureka 服务端后空闲等待时间,单位 秒,默认 30  
eureka.client.eureka-server-total-connections=200 :client 到 所有Eureka 服务端的连接总数,默认 200
eureka.client.eureka-server-total-connections-per-host=50 :client 到 Eureka 单服务端的连接总数,默认 50

微服务信息完善

application.yml文件里
eureka节点里可以添加

  instance:
    #Eureka 面板 显示服务名
    instance-id: 服务名
    #Eureka 面板 ,鼠标指向服务 显示ip地址信息
    prefer-ip-address: true

配置完成后,eureka视图里就直接显示自定义的服务名称了,鼠标悬浮会显示ip地址,这对于查看节点环境也是很方便的
在这里插入图片描述

参考文章:【SpringCloud】Eureka注册中心(二)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值