【JAVA企业级开发】通过一个基于REST的服务发现框架Euraka的开发,生产和运维案例,带你掌握SpringCloud微服务架构的微服务基础件Euraka的系统服务注册消费原理和应用过程

这里写目录标题

一级目录

二级目录

三级目录

一SpringCloud能干什么

Spring Cloud由众多子项目组成,如Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul 等,提供了搭建分布式系统及微服务常用的工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等,满足了构建微服务所需的所有解决方案。

服务发现——Netflix Eureka

客服端负载均衡——Netflix Ribbon

断路器——Netflix Hystrix

服务网关——Netflix Zuul

分布式配置——Spring Cloud Config

二Eureka能干什么

Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。

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

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

Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。

在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、

三Eureka案例

1父项目依赖

<?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>org.example</groupId>
    <artifactId>springcloud</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>commonApi</module>
        <module>provider</module>
        <module>consumer</module>
        <module>eureka-server</module>
    </modules>
    <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>
    </properties>
    <dependencyManagement>
        <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.0.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
    </dependencyManagement>


</project>

2 EurekaServer模块

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>FengboSoft</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>eureka-server</artifactId>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <!--eureka服务中心依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--cloud分布式配置中心-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <!--web组件        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
         <!--   &lt;!&ndash; 安全模式依赖 &ndash;&gt;
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>-->
    </dependencies>

</project>

启动类

package fengbo;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * Created by @author LiuChunhang on 2020/7/16.
 */
@SpringBootApplication
//启用服务中心
@EnableEurekaServer
public class EurekaServerApp {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(EurekaServerApp.class);
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
    }
}

配置文件application.properties

#服务端口号
server.port=8003
#eureka服务端实例URL
eureka.instance.hostname=localhost
#不向服务注册中心注册自己,如果是erueka集群的话,因为自我保护模式,就需要开启自注册了
eureka.client.register-with-eureka=false
#表示自己就是服务注册中心,职责就是维护服务,不去检索服务
eureka.client.fetch-registry=false
#服务中心地址
eureka.client.service-url.defaultZone= http://localhost:8003/eureka/
#调整保护机制生效的renews 和renews threshold百分比,此配置的百分比会减少 renews threshold的次数
eureka.server.renewal-percent-threshold=0.6
#安全模式访问服务中心,如果服务中心开启安全机制,则客户端的微服务注册请求和心跳机制将会被拦截
#spring.security.user.name=fengbo
#spring.security.user.password=fengbo


3EurekaClient模块

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>FengboSoft</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>eureka-client-account</artifactId>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <!--公共模块类依赖-->
        <dependency>
        <groupId>org.example</groupId>
        <artifactId>eureka-common-entity</artifactId>
        <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--springcloud的Finchley.SR2版本新的eureka客户端依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--eureka客户端启动配置依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <!--eureka的自我检查机制的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--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>1.3.0</version>
        </dependency>
        <!--mybatis分页插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!--阿里数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.3</version>
        </dependency>
    </dependencies>
</project>

controller,service,dao

package fengbo.controller;

import fengbo.entity.Account;
import fengbo.service.ProviderServiceInterFace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

/**
 * Created by @author LiuChunhang on 2020/7/15.
 */
@Controller
public class ProviderController {


    @Autowired
    public ProviderServiceInterFace providerServiceInterFace;
    public ProviderServiceInterFace getProviderServiceInterFace() {
        return providerServiceInterFace;
    }

    public void setProviderServiceInterFace(ProviderServiceInterFace providerServiceInterFace) {
        this.providerServiceInterFace = providerServiceInterFace;
    }

    @ResponseBody
    @RequestMapping(value = "login", method = {RequestMethod.POST, RequestMethod.GET})
    public List<Account> fengbo(){
        List<Account> validate = providerServiceInterFace.validate("yangshuang");
        for (Account account:validate){
            System.out.println(account);
        }
        return validate;
    }



}
package fengbo.service;

import fengbo.entity.Account;

import java.util.List;

/**
 * Created by @author LiuChunhang on 2020/7/14.
 */
public interface ProviderServiceInterFace {
    //插入成功并查询的业务
    public int register(Account account);
    // 验证查库
    public List validate(String string);

}

package fengbo.service;

import fengbo.dao.AccountMapper;
import fengbo.entity.Account;
import fengbo.entity.AccountExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * Created by @author LiuChunhang on 2020/7/15.
 */
@Service
public class ProviderService implements ProviderServiceInterFace {
    @Autowired
    public AccountMapper accountMapper;
    public AccountMapper getAccountMapper() {
        return accountMapper;
    }
    public void setAccountMapper(AccountMapper accountMapper) {
        this.accountMapper = accountMapper;
    }

    @Override
    @Transactional
    public int register(Account account) {

        int insert = accountMapper.insert(account);

        return insert;
    }

    @Override
    @Transactional
    public List validate(String string) {

        AccountExample accountExample = new AccountExample();

        AccountExample.Criteria criteria = accountExample.createCriteria();
        criteria.andAccountEqualTo(string);

        List<Account> accounts = accountMapper.selectByExample(accountExample);

        return accounts;
    }

}

/*@author*/
package fengbo.dao;
import fengbo.entity.Account;
import fengbo.entity.AccountExample;
import org.apache.ibatis.annotations.Param;
import java.util.List;

public interface AccountMapper {
    long countByExample(AccountExample example);

    int deleteByExample(AccountExample example);

    int deleteByPrimaryKey(Integer id);

    int insert(Account record);

    int insertSelective(Account record);

    List<Account> selectByExample(AccountExample example);

    Account selectByPrimaryKey(Integer id);

    int updateByExampleSelective(@Param("record") Account record, @Param("example") AccountExample example);

    int updateByExample(@Param("record") Account record, @Param("example") AccountExample example);

    int updateByPrimaryKeySelective(Account record);

    int updateByPrimaryKey(Account record);
}

启动类

package fengbo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * Created by @author LiuChunhang on 2020/7/14.
 */

@MapperScan(basePackages = "fengbo.dao")
@EnableTransactionManagement
@SpringBootApplication
@EnableEurekaClient
public class AccountClientApp {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(AccountClientApp.class);
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
    }
}

配置文件

mapper/AccountMapper.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="fengbo.dao.AccountMapper">
  <resultMap id="BaseResultMap" type="fengbo.entity.Account">
    <id column="Id" jdbcType="INTEGER" property="id" />
    <result column="account" jdbcType="VARCHAR" property="account" />
    <result column="password" jdbcType="VARCHAR" property="password" />
    <result column="enterprise" jdbcType="VARCHAR" property="enterprise" />
    <result column="phone" jdbcType="VARCHAR" property="phone" />
    <result column="mail" jdbcType="VARCHAR" property="mail" />
    <result column="status" jdbcType="INTEGER" property="status" />
  </resultMap>
  <sql id="Example_Where_Clause">
    <where>
      <foreach collection="oredCriteria" item="criteria" separator="or">
        <if test="criteria.valid">
          <trim prefix="(" prefixOverrides="and" suffix=")">
            <foreach collection="criteria.criteria" item="criterion">
              <choose>
                <when test="criterion.noValue">
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue">
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue">
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue">
                  and ${criterion.condition}
                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause">
    <where>
      <foreach collection="example.oredCriteria" item="criteria" separator="or">
        <if test="criteria.valid">
          <trim prefix="(" prefixOverrides="and" suffix=")">
            <foreach collection="criteria.criteria" item="criterion">
              <choose>
                <when test="criterion.noValue">
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue">
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue">
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue">
                  and ${criterion.condition}
                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List">
    Id, account, password, enterprise, phone, mail,status
  </sql>
  <select id="selectByExample" parameterType="fengbo.entity.AccountExample" resultMap="BaseResultMap">
    select
    <if test="distinct">
      distinct
    </if>
    <include refid="Base_Column_List" />
    from account
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null">
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from account
    where Id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from account
    where Id = #{id,jdbcType=INTEGER}
  </delete>
  <delete id="deleteByExample" parameterType="fengbo.entity.AccountExample">
    delete from account
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="fengbo.entity.Account">
    insert into account (Id, account, password, 
      enterprise, phone, mail,status
      )
    values (#{id,jdbcType=INTEGER}, #{account,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, 
      #{enterprise,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR}, #{mail,jdbcType=VARCHAR},#{status,jdbcType=INTEGER}
      )
  </insert>
  <insert id="insertSelective" parameterType="fengbo.entity.Account">
    insert into account
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        Id,
      </if>
      <if test="account != null">
        account,
      </if>
      <if test="password != null">
        password,
      </if>
      <if test="enterprise != null">
        enterprise,
      </if>
      <if test="phone != null">
        phone,
      </if>
      <if test="mail != null">
        mail,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="account != null">
        #{account,jdbcType=VARCHAR},
      </if>
      <if test="password != null">
        #{password,jdbcType=VARCHAR},
      </if>
      <if test="enterprise != null">
        #{enterprise,jdbcType=VARCHAR},
      </if>
      <if test="phone != null">
        #{phone,jdbcType=VARCHAR},
      </if>
      <if test="mail != null">
        #{mail,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="fengbo.entity.AccountExample" resultType="java.lang.Long">
    select count(*) from account
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map">
    update account
    <set>
      <if test="record.id != null">
        Id = #{record.id,jdbcType=INTEGER},
      </if>
      <if test="record.account != null">
        account = #{record.account,jdbcType=VARCHAR},
      </if>
      <if test="record.password != null">
        password = #{record.password,jdbcType=VARCHAR},
      </if>
      <if test="record.enterprise != null">
        enterprise = #{record.enterprise,jdbcType=VARCHAR},
      </if>
      <if test="record.phone != null">
        phone = #{record.phone,jdbcType=VARCHAR},
      </if>
      <if test="record.mail != null">
        mail = #{record.mail,jdbcType=VARCHAR},
      </if>
    </set>
    <if test="_parameter != null">
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map">
    update account
    set Id = #{record.id,jdbcType=INTEGER},
      account = #{record.account,jdbcType=VARCHAR},
      password = #{record.password,jdbcType=VARCHAR},
      enterprise = #{record.enterprise,jdbcType=VARCHAR},
      phone = #{record.phone,jdbcType=VARCHAR},
      mail = #{record.mail,jdbcType=VARCHAR}
    <if test="_parameter != null">
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="fengbo.entity.Account">
    update account
    <set>
      <if test="account != null">
        account = #{account,jdbcType=VARCHAR},
      </if>
      <if test="password != null">
        password = #{password,jdbcType=VARCHAR},
      </if>
      <if test="enterprise != null">
        enterprise = #{enterprise,jdbcType=VARCHAR},
      </if>
      <if test="phone != null">
        phone = #{phone,jdbcType=VARCHAR},
      </if>
      <if test="mail != null">
        mail = #{mail,jdbcType=VARCHAR},
      </if>
    </set>
    where Id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="fengbo.entity.Account">
    update account
    set account = #{account,jdbcType=VARCHAR},
      password = #{password,jdbcType=VARCHAR},
      enterprise = #{enterprise,jdbcType=VARCHAR},
      phone = #{phone,jdbcType=VARCHAR},
      mail = #{mail,jdbcType=VARCHAR}
    where Id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

mybatisConfig.xml

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

</configuration>

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,%i索引【从数字0开始递增】-->
    <!-- appender是configuration的子节点,是负责写日志的组件。 -->
    <!-- ConsoleAppender:把日志输出到控制台 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d %p (%file:%line\)- %m%n</pattern>
            <!-- 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
    <!-- 以下的大概意思是:1.先按日期存日志,日期变了,将前一天的日志文件名重命名为XXX%日期%索引,新的日志仍然是demo.log -->
    <!-- 2.如果日期没有发生变化,但是当前日志的文件大小超过1KB时,对当前日志进行分割 重命名 -->
    <appender name="demolog"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>demo.log</File>
        <!-- rollingPolicy:当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名。 -->
        <!-- TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 活动文件的名字会根据fileNamePattern的值,每隔一段时间改变一次 -->
            <!-- 文件名:log/demo.2018-12-05.0.log -->
            <fileNamePattern>log/demo.%d.%i.log</fileNamePattern>
            <!-- 每产生一个日志文件,该日志文件的保存期限为30-->
            <maxHistory>30</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- maxFileSize:这是活动文件的大小,默认值是10MB,测试时可改成1KB看效果 -->
                <maxFileSize>1KB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <!-- pattern节点,用来设置日志的输入格式 -->
            <pattern>
                %d %p (%file:%line\)- %m%n
            </pattern>
            <!-- 记录日志的编码:此处设置字符集 - -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 控制台输出日志级别 -->
    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
    <!-- 指定项目中某个包,当有日志操作行为时的日志记录级别 -->
    <!-- com.alibaba为根包,也就是只要是发生在这个根包下面的所有日志操作行为的权限都是DEBUG -->
    <!-- 级别依次为【从高到低】:FATAL > ERROR > WARN > INFO > DEBUG > TRACE -->

    <logger name="fengbo.dao" level="info">
        <appender-ref ref="demolog" />
    </logger>
</configuration>


#应用名
spring.application.name=FengBoProvider
#服务器端口
server.port=8001
#配置Mybatis
mybatis.config-location=classpath:mybatisConfig.xml
mybatis.mapper-locations=classpath:mapper/*.xml
#配置数据源bean
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=shuang666
spring.datasource.url=jdbc:mysql://49.235.16.100:3306/springcloud1
spring.datasource.dbcp2.min-idle=5
spring.datasource.dbcp2.initial-size=5
spring.datasource.dbcp2.max-idle=5
spring.datasource.dbcp2.max-wait-millis=200
#配置内置日志logback
logging.config=classpath:logback.xml
#配置单文件上传最大值,#配置多文件请求上传最大值
spring.servlet.multipart.max-file-size=6MB
spring.servlet.multipart.max-request-size=66MB
#分页插件配置,#指定数据库,#分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页,#支持通过 Mapper 接口参数来传递分页参数,默认值false
#pagehelper.helper-dialect=mysql
#pagehelper.reasonable=true
#pagehelper.support-methods-arguments=true
#注意不要忘了/eureka/后缀,设置与eureka-server的交互地址
eureka.client.service-url.defaultZone= http://localhost:8003/eureka/
#eureka的健康检查机制开关
eureka.client.healthcheck.enabled=true
#自我保护机制
#eureka.server.enable-self-preservation=false
#自我保护机制关闭之后的配置,告诉服务端,如果你N秒之后还没有接受到我发送的心跳,就代表我"死"了,将我的服务踢出掉。
#eureka.instance.lease-expiration-duration-in-seconds=36
#设置每隔多少秒给服务中心发送一次心跳,证明自己还活着
#eureka.instance.lease-renewal-interval-in-seconds=6
#springcloud的配置中心开关,SpringCloud默认启动的时候是会先加载bootstrap.properties或bootstrap.yml配置文件,如果没有的话,则会远程从http://localhost:8888获取配置,然后才会加载到application.yml,properties文件。
spring.cloud.config.enabled=false


4 公共模块commonApi

1pom

<?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>springcloud</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>commonApi</artifactId>


</project>

2实体类

package fengbo.entity;

import java.io.Serializable;

/**
 * Created by @author LiuChunhang on 2020/7/14.
 */
public class  Account  implements Serializable {
        private Integer id;

        private String account;

        private String password;

        private String enterprise;


        private String phone;

        private String mail;

        private int status;
        public Account() {
        }
        public Account(Integer id, String account, String password, String enterprise, String phone, String mail, int status) {
            this.id = id;
            this.account = account;
            this.password = password;
            this.enterprise = enterprise;
            this.phone = phone;
            this.mail = mail;
            this.status = status;
        }



        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getAccount() {
            return account;
        }

        public void setAccount(String account) {
            this.account = account == null ? null : account.trim();
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password == null ? null : password.trim();
        }

        public String getEnterprise() {
            return enterprise;
        }

        public void setEnterprise(String enterprise) {
            this.enterprise = enterprise == null ? null : enterprise.trim();
        }

        public String getPhone() {
            return phone;
        }

        public void setPhone(String phone) {
            this.phone = phone == null ? null : phone.trim();
        }

        public String getMail() {
            return mail;
        }

        public void setMail(String mail) {
            this.mail = mail == null ? null : mail.trim();
        }
        public int getStatus() {
            return status;
        }

        public void setStatus(int status) {
            this.status = status;
        }

        @Override
        public String toString() {
            return "Account{" +
                    "id=" + id +
                    ", account='" + account + '\'' +
                    ", password='" + password + '\'' +
                    ", enterprise='" + enterprise + '\'' +
                    ", phone='" + phone + '\'' +
                    ", mail='" + mail + '\'' +
                    ", status=" + status +
                    '}';
        }



}

package fengbo.entity;

import java.util.ArrayList;
import java.util.List;

public class AccountExample {
    protected String orderByClause;

    protected boolean distinct;

    protected List<Criteria> oredCriteria;

    public AccountExample() {
        oredCriteria = new ArrayList<>();
    }

    public void setOrderByClause(String orderByClause) {
        this.orderByClause = orderByClause;
    }

    public String getOrderByClause() {
        return orderByClause;
    }

    public void setDistinct(boolean distinct) {
        this.distinct = distinct;
    }

    public boolean isDistinct() {
        return distinct;
    }

    public List<Criteria> getOredCriteria() {
        return oredCriteria;
    }

    public void or(Criteria criteria) {
        oredCriteria.add(criteria);
    }

    public Criteria or() {
        Criteria criteria = createCriteriaInternal();
        oredCriteria.add(criteria);
        return criteria;
    }

    public Criteria createCriteria() {
        Criteria criteria = createCriteriaInternal();
        if (oredCriteria.size() == 0) {
            oredCriteria.add(criteria);
        }
        return criteria;
    }

    protected Criteria createCriteriaInternal() {
        Criteria criteria = new Criteria();
        return criteria;
    }

    public void clear() {
        oredCriteria.clear();
        orderByClause = null;
        distinct = false;
    }

    protected abstract static class GeneratedCriteria {
        protected List<Criterion> criteria;

        protected GeneratedCriteria() {
            super();
            criteria = new ArrayList<>();
        }

        public boolean isValid() {
            return criteria.size() > 0;
        }

        public List<Criterion> getAllCriteria() {
            return criteria;
        }

        public List<Criterion> getCriteria() {
            return criteria;
        }

        protected void addCriterion(String condition) {
            if (condition == null) {
                throw new RuntimeException("Value for condition cannot be null");
            }
            criteria.add(new Criterion(condition));
        }

        protected void addCriterion(String condition, Object value, String property) {
            if (value == null) {
                throw new RuntimeException("Value for " + property + " cannot be null");
            }
            criteria.add(new Criterion(condition, value));
        }

        protected void addCriterion(String condition, Object value1, Object value2, String property) {
            if (value1 == null || value2 == null) {
                throw new RuntimeException("Between values for " + property + " cannot be null");
            }
            criteria.add(new Criterion(condition, value1, value2));
        }

        public Criteria andIdIsNull() {
            addCriterion("Id is null");
            return (Criteria) this;
        }

        public Criteria andIdIsNotNull() {
            addCriterion("Id is not null");
            return (Criteria) this;
        }

        public Criteria andIdEqualTo(Integer value) {
            addCriterion("Id =", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdNotEqualTo(Integer value) {
            addCriterion("Id <>", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdGreaterThan(Integer value) {
            addCriterion("Id >", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdGreaterThanOrEqualTo(Integer value) {
            addCriterion("Id >=", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdLessThan(Integer value) {
            addCriterion("Id <", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdLessThanOrEqualTo(Integer value) {
            addCriterion("Id <=", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdIn(List<Integer> values) {
            addCriterion("Id in", values, "id");
            return (Criteria) this;
        }

        public Criteria andIdNotIn(List<Integer> values) {
            addCriterion("Id not in", values, "id");
            return (Criteria) this;
        }

        public Criteria andIdBetween(Integer value1, Integer value2) {
            addCriterion("Id between", value1, value2, "id");
            return (Criteria) this;
        }

        public Criteria andIdNotBetween(Integer value1, Integer value2) {
            addCriterion("Id not between", value1, value2, "id");
            return (Criteria) this;
        }

        public Criteria andAccountIsNull() {
            addCriterion("account is null");
            return (Criteria) this;
        }

        public Criteria andAccountIsNotNull() {
            addCriterion("account is not null");
            return (Criteria) this;
        }

        public Criteria andAccountEqualTo(String value) {
            addCriterion("account =", value, "account");
            return (Criteria) this;
        }

        public Criteria andAccountNotEqualTo(String value) {
            addCriterion("account <>", value, "account");
            return (Criteria) this;
        }

        public Criteria andAccountGreaterThan(String value) {
            addCriterion("account >", value, "account");
            return (Criteria) this;
        }

        public Criteria andAccountGreaterThanOrEqualTo(String value) {
            addCriterion("account >=", value, "account");
            return (Criteria) this;
        }

        public Criteria andAccountLessThan(String value) {
            addCriterion("account <", value, "account");
            return (Criteria) this;
        }

        public Criteria andAccountLessThanOrEqualTo(String value) {
            addCriterion("account <=", value, "account");
            return (Criteria) this;
        }

        public Criteria andAccountLike(String value) {
            addCriterion("account like", value, "account");
            return (Criteria) this;
        }

        public Criteria andAccountNotLike(String value) {
            addCriterion("account not like", value, "account");
            return (Criteria) this;
        }

        public Criteria andAccountIn(List<String> values) {
            addCriterion("account in", values, "account");
            return (Criteria) this;
        }

        public Criteria andAccountNotIn(List<String> values) {
            addCriterion("account not in", values, "account");
            return (Criteria) this;
        }

        public Criteria andAccountBetween(String value1, String value2) {
            addCriterion("account between", value1, value2, "account");
            return (Criteria) this;
        }

        public Criteria andAccountNotBetween(String value1, String value2) {
            addCriterion("account not between", value1, value2, "account");
            return (Criteria) this;
        }

        public Criteria andPasswordIsNull() {
            addCriterion("password is null");
            return (Criteria) this;
        }

        public Criteria andPasswordIsNotNull() {
            addCriterion("password is not null");
            return (Criteria) this;
        }

        public Criteria andPasswordEqualTo(String value) {
            addCriterion("password =", value, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordNotEqualTo(String value) {
            addCriterion("password <>", value, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordGreaterThan(String value) {
            addCriterion("password >", value, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordGreaterThanOrEqualTo(String value) {
            addCriterion("password >=", value, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordLessThan(String value) {
            addCriterion("password <", value, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordLessThanOrEqualTo(String value) {
            addCriterion("password <=", value, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordLike(String value) {
            addCriterion("password like", value, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordNotLike(String value) {
            addCriterion("password not like", value, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordIn(List<String> values) {
            addCriterion("password in", values, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordNotIn(List<String> values) {
            addCriterion("password not in", values, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordBetween(String value1, String value2) {
            addCriterion("password between", value1, value2, "password");
            return (Criteria) this;
        }

        public Criteria andPasswordNotBetween(String value1, String value2) {
            addCriterion("password not between", value1, value2, "password");
            return (Criteria) this;
        }

        public Criteria andEnterpriseIsNull() {
            addCriterion("enterprise is null");
            return (Criteria) this;
        }

        public Criteria andEnterpriseIsNotNull() {
            addCriterion("enterprise is not null");
            return (Criteria) this;
        }

        public Criteria andEnterpriseEqualTo(String value) {
            addCriterion("enterprise =", value, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseNotEqualTo(String value) {
            addCriterion("enterprise <>", value, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseGreaterThan(String value) {
            addCriterion("enterprise >", value, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseGreaterThanOrEqualTo(String value) {
            addCriterion("enterprise >=", value, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseLessThan(String value) {
            addCriterion("enterprise <", value, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseLessThanOrEqualTo(String value) {
            addCriterion("enterprise <=", value, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseLike(String value) {
            addCriterion("enterprise like", value, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseNotLike(String value) {
            addCriterion("enterprise not like", value, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseIn(List<String> values) {
            addCriterion("enterprise in", values, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseNotIn(List<String> values) {
            addCriterion("enterprise not in", values, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseBetween(String value1, String value2) {
            addCriterion("enterprise between", value1, value2, "enterprise");
            return (Criteria) this;
        }

        public Criteria andEnterpriseNotBetween(String value1, String value2) {
            addCriterion("enterprise not between", value1, value2, "enterprise");
            return (Criteria) this;
        }

        public Criteria andPhoneIsNull() {
            addCriterion("phone is null");
            return (Criteria) this;
        }

        public Criteria andPhoneIsNotNull() {
            addCriterion("phone is not null");
            return (Criteria) this;
        }

        public Criteria andPhoneEqualTo(String value) {
            addCriterion("phone =", value, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneNotEqualTo(String value) {
            addCriterion("phone <>", value, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneGreaterThan(String value) {
            addCriterion("phone >", value, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneGreaterThanOrEqualTo(String value) {
            addCriterion("phone >=", value, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneLessThan(String value) {
            addCriterion("phone <", value, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneLessThanOrEqualTo(String value) {
            addCriterion("phone <=", value, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneLike(String value) {
            addCriterion("phone like", value, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneNotLike(String value) {
            addCriterion("phone not like", value, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneIn(List<String> values) {
            addCriterion("phone in", values, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneNotIn(List<String> values) {
            addCriterion("phone not in", values, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneBetween(String value1, String value2) {
            addCriterion("phone between", value1, value2, "phone");
            return (Criteria) this;
        }

        public Criteria andPhoneNotBetween(String value1, String value2) {
            addCriterion("phone not between", value1, value2, "phone");
            return (Criteria) this;
        }

        public Criteria andMailIsNull() {
            addCriterion("mail is null");
            return (Criteria) this;
        }

        public Criteria andMailIsNotNull() {
            addCriterion("mail is not null");
            return (Criteria) this;
        }

        public Criteria andMailEqualTo(String value) {
            addCriterion("mail =", value, "mail");
            return (Criteria) this;
        }

        public Criteria andMailNotEqualTo(String value) {
            addCriterion("mail <>", value, "mail");
            return (Criteria) this;
        }

        public Criteria andMailGreaterThan(String value) {
            addCriterion("mail >", value, "mail");
            return (Criteria) this;
        }

        public Criteria andMailGreaterThanOrEqualTo(String value) {
            addCriterion("mail >=", value, "mail");
            return (Criteria) this;
        }

        public Criteria andMailLessThan(String value) {
            addCriterion("mail <", value, "mail");
            return (Criteria) this;
        }

        public Criteria andMailLessThanOrEqualTo(String value) {
            addCriterion("mail <=", value, "mail");
            return (Criteria) this;
        }

        public Criteria andMailLike(String value) {
            addCriterion("mail like", value, "mail");
            return (Criteria) this;
        }

        public Criteria andMailNotLike(String value) {
            addCriterion("mail not like", value, "mail");
            return (Criteria) this;
        }

        public Criteria andMailIn(List<String> values) {
            addCriterion("mail in", values, "mail");
            return (Criteria) this;
        }

        public Criteria andMailNotIn(List<String> values) {
            addCriterion("mail not in", values, "mail");
            return (Criteria) this;
        }

        public Criteria andMailBetween(String value1, String value2) {
            addCriterion("mail between", value1, value2, "mail");
            return (Criteria) this;
        }

        public Criteria andMailNotBetween(String value1, String value2) {
            addCriterion("mail not between", value1, value2, "mail");
            return (Criteria) this;
        }
    }

    public static class Criteria extends GeneratedCriteria {
        protected Criteria() {
            super();
        }
    }

    public static class Criterion {
        private String condition;

        private Object value;

        private Object secondValue;

        private boolean noValue;

        private boolean singleValue;

        private boolean betweenValue;

        private boolean listValue;

        private String typeHandler;

        public String getCondition() {
            return condition;
        }

        public Object getValue() {
            return value;
        }

        public Object getSecondValue() {
            return secondValue;
        }

        public boolean isNoValue() {
            return noValue;
        }

        public boolean isSingleValue() {
            return singleValue;
        }

        public boolean isBetweenValue() {
            return betweenValue;
        }

        public boolean isListValue() {
            return listValue;
        }

        public String getTypeHandler() {
            return typeHandler;
        }

        protected Criterion(String condition) {
            super();
            this.condition = condition;
            this.typeHandler = null;
            this.noValue = true;
        }

        protected Criterion(String condition, Object value, String typeHandler) {
            super();
            this.condition = condition;
            this.value = value;
            this.typeHandler = typeHandler;
            if (value instanceof List<?>) {
                this.listValue = true;
            } else {
                this.singleValue = true;
            }
        }

        protected Criterion(String condition, Object value) {
            this(condition, value, null);
        }

        protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
            super();
            this.condition = condition;
            this.value = value;
            this.secondValue = secondValue;
            this.typeHandler = typeHandler;
            this.betweenValue = true;
        }

        protected Criterion(String condition, Object value, Object secondValue) {
            this(condition, value, secondValue, null);
        }
    }
}

四测试

在这里插入图片描述
Eureka server和client之间每隔30秒会进行一次心跳通信,告诉server,client还活着。由此引出两个名词:
Renews threshold:server期望在每分钟中收到的心跳次数
Renews (last min):上一分钟内收到的心跳次数。

Eurake有一个配置参数eureka.server.renewalPercentThreshold,定义了renews 和renews threshold的比值,默认值为0.85。当server在15分钟内,比值低于percent,即少了15%的微服务心跳,server会进入自我保护状态,Self-Preservation。在此状态下,server不会删除注册信息,这就有可能导致在调用微服务时,实际上服务并不存在。
这种保护状态实际上是考虑了client和server之间的心跳是因为网络问题

stackoverflow上,有人给出的建议是:
1、在生产上可以开自注册,部署两个server
2、在本机器上测试的时候,可以把比值调低,比如0.49
可以通过以下参数进行调整:

#调整保护机制生效的renews 和renews threshold百分比
eureka.server.renewal-percent-threshold=0.6

3、或者简单粗暴把自我保护模式关闭

五Eureka在应用过程中的警告解决

1

警告1
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

解决这种情况的方法主要有几种方式:

  1. 等待 Eureka Server 自动恢复
    正常的情况下,等待网络恢复(或者没有频繁的启动与关闭实例)后,等待一段时间 Eureka Server 会自动关闭自我保护模式,但是如果它迟迟没有关闭该模式,那么便可以尝试手动关闭,如下。

  2. 重启 Eureka Server
    通常而言,PRD 环境建议对 Eureka Server 做负载均衡,这样在依次关闭并开启 Eureka Server 后,无效的实例会被清除,并且不会对正常的使用照成影响。

  3. 关闭 Eureka 的自我保护模式

# 关闭 Eureka 的自我保护模式
eureka.server.enable-self-preservation=false
#关闭自我保护模式以后一般要加上这个配置
eureka.instance.lease-expiration-duration-in-seconds=30

2

警告2
THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIR 

可能
server端服务中心地址和client端的服务中心地址不一样,没有达到连通效果,仔细检查你的server端和client端application.properties文件

六 关于Eureka的自我保护模式

1引入自我保护模式

首先对Eureka注册中心需要了解的是Eureka各个节点都是平等的,没有ZK中角色的概念, 即使N-1个节点挂掉也不会影响其他节点的正常运行。
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。

2自我保护机制定义

官方对于自我保护机制的定义:
自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。
详细源码解读Renews (last min) < Renews threshold

在15分钟(这个十五分钟是一个eruakaserver重新计算心跳阈值的时间窗口getRenewalThresholdUpdateIntervalMs默认值为15分钟)内超过85%的客户端节点仍然都没有正常的心跳(Renews (last min) < Renews threshold的意思就是注册在某一个eurekaserver中的很多个微服务客户端在上一分钟发送给自己的心跳小于默认值的百分之85),那么Eureka就认为客户端与注册中心出现了网络故障,如果Renews (last min) < Renews threshold,默认需等待 5 分钟,eureka服务server它等待客户端几分钟(5分钟),以便客户端可以注册他们的信息。(可以通过eureka.server.wait-time-in-ms-when-sync-empty配置),即 5 分钟后如果客户端还没有像服务器注册它的信息,Eureka Server自动进入自我保护机制,你会看到下面的提示信息:

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

此时这个server会出现以下几种情况:

Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。

Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。

当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。

因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像ZK那样如果有一半不可用的情况会导致整个集群不可用而变成瘫痪。Eurake有一个配置参数eureka.server.renewalPercentThreshold,定义了renews 和renews threshold的比值,默认值为0.85。当server在15分钟内,比值低于percent,即少了15%的微服务心跳,server会进入自我保护状态,Self-Preservation。在此状态下,server不会删除注册信息,这就有可能导致在调用微服务时,实际上服务并不存在。
这种保护状态实际上是考虑了client和server之间的心跳是因为网络问题,而非服务本身问题,不能简单的删除注册信息.

3关于自我保护机制心跳和阈值的问题的计算和解决问题的方法,这里涉及到eureka集群,请先看我的下一篇构建分布式eureka集群得的文章https://blog.csdn.net/liuchunhang/article/details/107513182

①阈值renews threshold

前文说到禁止注册server自己为client,不管server是否禁止自注册,不管集群中有多少个server相互注册,都不会对阈值构成影响,server的阈值(threshold)一直是1。

开启三台server自注册,阈值依然为1
在这里插入图片描述
如果client个数为n,阈值为的计算方式就要变成 1+2×n*0.85
关闭自注册,且client的个数为1,则阈值就等于1+210.85=3(阈值计算四舍五入)
在这里插入图片描述

②心跳值renews

如果多个server,且开启了自注册,虽然不会对阈值renews threshold有影响,但是这些就和client一样,是对于其他的server来说就是client

我开了三个server,一个服务客户端,自注册开启和关闭,相关配置如下
我阈值的百分比renews threshold从默认的0.85调整到了0.6,每分钟的心跳次数eureka.instance.lease-renewal-interval-in-seconds还是按照系统给的默认值。

#阈值比例
eureka.server.renewal-percent-threshold=0.5
#(30就是默认值,每分钟两次心跳)
eureka.instance.lease-renewal-interval-in-seconds=30

(阈值计算四舍五入)renews threshold:(1+2×1)*0.6=2(自注册和非自注册的阈值都是2)
(实际心跳值)renews:

1)自注册 3+1乘以2=8 三个eurekaserver加一个serverprovider

在这里插入图片描述

2)非自注册:1乘以2=2 零个eurekaserver加一个serverprovider

在这里插入图片描述

4自我保护机制警告的解决和处理

stackoverflow上,有人给出的建议是:
1、在生产上可以开自注册,部署多个个server
2、在本机器上测试的时候,可以把比值调低,比如0.6

#调整保护机制生效的renews 和renews threshold百分比
eureka.server.renewal-percent-threshold=0.6

3、或者把自我保护模式关闭

#自我保护机制
#eureka.server.enable-self-preservation=false
#自我保护机制关闭之后的配置,告诉服务端,如果你N秒之后还没有接受到我发送的心跳,就代表我"死"了,将我的服务踢出掉。
#eureka.instance.lease-expiration-duration-in-seconds=36
#设置每隔多少秒给服务中心发送一次心跳,证明自己还活着
#eureka.instance.lease-renewal-interval-in-seconds=6

所以综上所述解决这个问题的方式有三种:

1关闭自我保护模式(eureka.server.enable-self-preservation设为false),

不推荐

2降低renewalPercentThreshold的比例(eureka.server.renewal-percent-threshold设置为0.5以下,比如0.49)

不推荐

3多个server开启自注册,并相互注册,部署多个 Eureka Server 并开启其客户端行为(eureka.client.register-with-eureka不要设为false,默认为true)

推荐

七 关于Eureka的健康检查机制

客户端需要在工程中添加组件依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

然后在配置文件里面加上健康状态检查配置:

eureka.client.healthcheck.enabled=true

这时,启动自己的服务,通过http://localhost:port/actuator/health
访问就可以得到服务的健康状态信息:
在这里插入图片描述
在这里插入图片描述
注意 `spring cloud的配置信息加载顺序是 bootstrap> 远程仓库(config server) > application
默认情况之下,如果不在bootstrap中配置 springcloud配置中心的url,客户端的健康检查会一直访问8888端口号,来调用默认的 config server的uri ,但是此uri在我们没有创建之前是不存在的,所以会一直报:

2020-07-20 10:10:24,342 INFO (ConfigServicePropertySourceLocator.java:205)- Fetching config from server at : http://localhost:8888
2020-07-20 10:10:24,394 INFO (ConfigServicePropertySourceLocator.java:205)- Fetching config from server at : http://localhost:8888
2020-07-20 10:10:26,347 INFO (ConfigServicePropertySourceLocator.java:227)- Connect Timeout Exception on Url - http://localhost:8888. Will be trying the next url if available
2020-07-20 10:10:26,348 WARN (ConfigServicePropertySourceLocator.java:140)- Could not locate PropertySource: I/O error on GET request for "http://localhost:8888/FengBoProvider/default": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect
...........................

解决此种情况有两种
1配置好可访问可供加载配置信息的 config server的URI(详细的方法和解读见下一章)
2关闭 config server

spring.cloud.config.enabled=false

默认情况下,Eureka使用客户端心跳来决定一个服务是否是处于“UP”状态的,只要客户端注册服务成功以后,Eureka服务器端就宣布这个服务是“UP”的,所以,如果是服务整个宕掉了,还好说,Eureka服务器能够知道这个服务挂掉了,但是倘若一个服务提供者不能进行数据库连接了,这个服务实例就是不可用的,但我们的服务器可不这么认为,因为他也无从知晓这个服务是有问题的。所以,我们这里引入Actuator,并使用它的\Health端点做健康检测。

我们在程序内部模仿数据库连接,并在控制器提供一个方法,用于修改数据库连接是否发生了故障,之后通知health端点,修改当前服务的状态。在ProviderController类添加下面的方法:

public static Boolean isCanLinkDb = true;

@RequestMapping(value = "dbstatus", method = RequestMethod.GET)
public void LinkDb(@PathVariable Boolean status){
	isCanLinkDb = status;
}

之后新建一个MyHealthIndicator类,实现HealthIndicator接口,重写Health方法,把数据库是否能连接这个状态传入,最后更改当前服务的健康状态:

package com.init.springCloud;
 
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;
/**
 * 自定义健康指示器(Spring Boot的内容)
 * @author spirit   
 * @date 2018年5月3日 下午2:19:58 
 * @email spirit612@sina.cn
 */
@Component
public class MyHealthIndicator implements HealthIndicator {
 
	@Override
	public Health health() {
		if(ProviderController.isCanLinkDb){
			return new Health.Builder(Status.UP).build();
		}else{
			return new Health.Builder(Status.DOWN).build();
		}
	}
 
}

这里修改了服务的状态,也是针对于eureka-provider自身来说的,我们能够通过health端点知道服务是否是正常的,那怎么能把health端点返回的服务状态告诉eureka-server呢?也就是跟Eureka服务器说明,我这个服务的数据库不能连接了,服务暂时不可以使用了,你在服务列表里面把我这个服务的状态更改到“DOWN”状态,不要继续让请求涌入我这个服务。这里我们使用Netflix里面提供的HealthCheckHandler接口来做,新建一个MyHealthCheckHandler类,实现HealthCheckHandler接口,重写getStatus()方法:

package com.init.springCloud;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;
 
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
 
/**
 * 健康检查处理器
 * @author spirit   
 * @date 2018年5月3日 下午2:39:45 
 * @email spirit612@sina.cn
 */
@Component
public class MyHealthCheckHandler implements HealthCheckHandler {
 
	@Autowired
	private MyHealthIndicator myHealthIndicator;
	
	@Override
	public InstanceStatus getStatus(InstanceStatus instanceStatus) {
		Status status = myHealthIndicator.health().getStatus();
		if(status.equals(Status.UP)){
			return InstanceStatus.UP;
		}else{
			return InstanceStatus.DOWN;
		}
	}
 
}

其实,到这里,已经完成了所有的测试代码。但是,为了能让eureka-server接受到服务变更信息后,更快速地把服务列表的信息进行更改同步(多台发现服务的服务器之间会复制服务列表,进行更新),我们缩短一下更新的时间。

eureka.client.instance-info-replication-interval-seconds 表示复制实例更改的频率(以秒为单位),以复制到发现服务的服务器。默认30s。

在eureka-server的application.yml文件中添加上面的配置,把时间更改到10s:

eviction-interval-timer-in-ms: 10000

八消费者(模块)从注册中心调用微服务

1config类

package fengbo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**

  • Created by @author LiuChunhang on 2020/7/21.
    */
    @Configuration
    public class RestTemplateBean {
    @Bean
    public RestTemplate restTemplate(){
    RestTemplate restTemplate = new RestTemplate();
    return restTemplate;
    }
    }

2controller类

package fengbo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

/**
 * Created by @author LiuChunhang on 2020/7/21.
 */
@Controller
public class AccountController {
    @Autowired
    public RestTemplate restTemplate;
    public RestTemplate getRestTemplate() {
        return restTemplate;
    }
    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
    @ResponseBody
    @RequestMapping(value = "account/server")
    public Object index(){
       return restTemplate.getForObject("http://localhost:8001/login", Object.class);
    }
}

3启动类

package fengbo;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Created by @author LiuChunhang on 2020/7/21.
 */
@SpringBootApplication
public class AccountSystem {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(AccountSystem.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }
}

4配置文件application.properties

server.port=8080

5pom文件

<?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>FengboSoft</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-system-account</artifactId>

    <dependencies>
        <!--公共实体类API-->
        <dependency>
            <artifactId>eureka-common-entity</artifactId>
            <groupId>org.example</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--Web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

6测试

在这里插入图片描述

九 @EnableDiscoveryClient注解

@EnableDiscoveryClient@EnableEurekaClient共同点就是:都是能够让注册中心能够发现,扫描到改服务。

不同点:@EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient 可以是其他注册中心。

1在微服务客户端EurekaClient(见上)的启动类上加上这个注解

@EnableDiscoveryClient
package fengbo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * Created by @author LiuChunhang on 2020/7/14.
 */

@MapperScan(basePackages = "fengbo.dao")
@EnableTransactionManagement
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class AccountClientApp {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(AccountClientApp.class);
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
    }
}

2微服务客户端EurekaClient注入DiscoveryClientBean,并再创建处理映射器@RequestMapping(value = “accountserver”)

package fengbo.controller;

import fengbo.entity.Account;
import fengbo.service.AccountServiceInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
 * Created by @author LiuChunhang on 2020/7/15.
 */
@Controller
public class AccountController {
    @Autowired
    public AccountServiceInterface providerServiceInterFace;
    @Autowired
    public DiscoveryClient discoveryClient;
    public AccountServiceInterface getProviderServiceInterFace() {
        return providerServiceInterFace;
    }

    public void setProviderServiceInterFace(AccountServiceInterface providerServiceInterFace) {
        this.providerServiceInterFace = providerServiceInterFace;
    }
    public DiscoveryClient getDiscoveryClient() {
        return discoveryClient;
    }
    public void setDiscoveryClient(DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }

    @ResponseBody
    @RequestMapping(value = "login", method = {RequestMethod.POST, RequestMethod.GET})
    public List<Account> fengbo(){
        List<Account> validate = providerServiceInterFace.validate("yangshuang");
        for (Account account:validate){
            System.out.println(account);
        }
        return validate;
    }
    @ResponseBody
    @RequestMapping(value = "accountserver")
    public Object server(){
        List<String> services = discoveryClient.getServices();
        for (String string:services){
            System.out.println(string);
        }
        List<ServiceInstance> instances = discoveryClient.getInstances("accountProvider");
        for (ServiceInstance serviceInstance:instances){
            System.out.println(serviceInstance.getHost()+serviceInstance.getScheme()+serviceInstance.getServiceId()+serviceInstance.getMetadata()+serviceInstance.getPort()+serviceInstance.getUri());
        }
        return discoveryClient;
    }
}

3 消费者系统模块(具体内容见上)调用微服务,在controller中再创建处理映射器@RequestMapping(value = “accountserver”)

 @RequestMapping(value = "account/server")
    public Object index(){
       return restTemplate.getForObject("http://localhost:8001/accountserver", Object.class);
    }

4测试(分别按先后启动 server,client,consumer)

在这里插入图片描述

在这里插入图片描述

十spring Cloud Eureka增加security后注册失败解决方案

1 依赖

 <!-- 安全模式依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

2配置

#应用名
spring.application.name=eurekaserver1
#服务端口号
server.port=9001
#eureka服务端实例URL
eureka.instance.hostname=eurekaserver1
#不向服务注册中心注册自己,如果是erueka集群的话,因为自我保护模式,就需要开启自注册了
eureka.client.register-with-eureka=true
#表示自己就是服务注册中心,职责就是维护服务,不去检索服务
eureka.client.fetch-registry=false
#服务中心地址
eureka.client.service-url.defaultZone=http://eurekaserver2:9002/eureka/,http://eurekaserver3:9003/eureka/
#调整保护机制生效的renews 和renews threshold百分比,此配置的百分比会减少 renews threshold的次数
eureka.server.renewal-percent-threshold=0.85
#安全模式访问服务中心,如果服务中心开启安全机制,则客户端的微服务注册请求和心跳机制将会被拦截
spring.security.user.roles=server1
spring.security.user.name=fengbo
spring.security.user.password=fengbo

3启动类

启动类上增加一个内部类

  @EnableWebSecurity
        class WebSecurityConfig extends WebSecurityConfigurerAdapter {
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http.csrf().ignoringAntMatchers("/eureka/**");
                super.configure(http);
            }
        }
package fengbo;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * Created by @author LiuChunhang on 2020/7/18.
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer1 {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(EurekaServer1.class);
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
        @EnableWebSecurity
        class WebSecurityConfig extends WebSecurityConfigurerAdapter {
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http.csrf().ignoringAntMatchers("/eureka/**");
                super.configure(http);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牵牛刘先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值