微服务之Eureka组件


概念

背景
传统的单体应用开发,就是将api和app的代码全部集成在一起,在同一个进程中运行,对应java web通俗的说就是全部打包在一个war中部署在一个tomcat中运行。而微服务的每个api,app都拥有自己的进程,也就是都有自己的tomcat,某个api挂了,不影响其他api和app运行。
作用

  1. Eureka Server 提供服务发现的功能, 各个微服务会将自己的信息注册到Eureka Server。
  2. Eureka Client 即服务提供者,将其信息注册到Eureka Server上面。
  3. 微服务会周期性(默认30秒)地向Eureka Server 发送心跳以维持自己的注册状态,如果Eureka Server 在一定时间(默认90秒)没有接受到某个微服务实例的心跳,Eureka Server 将会注销该实例。
  4. 默认情况下,Eureka Server 同时也是 Client, 多个Eureka Server 实例之间可以通过复制的方法, 来实现服务注册表数据的同步。
  5. Eureka Client 会缓存服务注册表中的信息,所以 Eureka Client 无须每次调用微服务都要先查询Eureka Server,能有效地缓解Eureka Server的压力,而且即使所有的Eureka Server节点都宕掉,Client 依然可以根据缓存中信息找到服务提供者。

Eureka微服务(单机版)

1、建Module

2、改pom.xml

<!--2022年2月发布的Eureka服务端组件-->
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
   <version>3.1.1</version>
</dependency>

3、写yml

server:
  port: 7001

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

#spring 2021必须配置dataresource
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    #mysql5.x的没有cj
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root123

4、主启动

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

5、业务类(Provider注册到Eureka服务中心)

补充pom.xml

<!--2022年2月发布的Eureka客户端组件-->
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
   <version>3.1.1</version>
</dependency>

补充yml文件

eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka  #Eureka服务端的地址
  server:
    # 默认值是true
    enable-self-preservation: true
    # default is 0.85 we can set the value to 0.49
    renewalPercentThreshold: 0.49

修改主启动类(添加Eureka注解)

@SpringBootApplication
@EnableEurekaClient      // 添加eureka注解
public class PaymentMain {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain.class,args);
    }
}

Eureka微服务(集群版)

新建Eureka7010 Module和Eureka7011 Module

以Eureka7010为例,Eureka7011同理

改pom.xml

        <!--Eureka服务端组件-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

修改hosts文件配置文件

写yml(互相注册,相互守望)

server:
  port: 7010

eureka:
  instance:
    hostname: eureka-7010.com #eureka服务端的实例名称
    leaseRenewalIntervalInSeconds: 30 # default is 30

  client:
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka-7011.com:7011/eureka
  server:
    # 默认值是true
    enable-self-preservation: true
    # default is 0.85 we can set the value to 0.49
    renewalPercentThreshold: 0.49

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    #mysql5.x的没有cj
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root123

主启动

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

两台Eureka集群搭建完成效果

将微支付和消费者注册到Eureka服务中心

修改微支付和消费者的application.yml文件

eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka  #Eureka服务端的地址(单机版)
      defaultZone: http://eureka-7010.com:7010/eureka, http://eureka-7011.com:7011/eureka  #Eureka服务端的地址(集群版)

效果如下图

Eureka客户端集群

新建两个Module(payment8090,payment8091)

以payment8090为例

改pom.xml

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

    <artifactId>cloud-provider-payment</artifactId>

<!--子类需要自己写版本号,如果父pom文件声明了版本号则子类不用声明版本号-->

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

        <dependency>
            <groupId>com.tl.com.tl.com.tl.springcloud</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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!-- spring-boot-devtools 热部署工具 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
</project>

写yml(即改server port—>8090)

server:
  port: 8090

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
      #mysql5.x的没有cj
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/srcfxczou?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root123


mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.tl.com.tl.com.tl.springcloud.entities    # 所有Entity别名类所在包

eureka:
  client:
    #表示是否将自己注册进Eurekaserver默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka  #Eureka服务端的地址(单机版)
      defaultZone: http://eureka-7010.com:7010/eureka, http://eureka-7011.com:7011/eureka  #Eureka服务端的地址(集群版)

主启动类

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

业务类(修改Controller)

修改处1:@Value(“${server.port}”)
       private String serverPort;//添加serverPort
修改处2:return new CommonResult(200, "插入成功,serverPort: "+serverPort, payment);   【create()的变动】
修改处3:return new CommonResult(200, "查询成功,serverPort: "+serverPort, payment);   【getPaymentById()的变动】

@RestController
public class PaymentController {

    @Resource
    PaymentService paymentService;

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

    @PostMapping("/payment/create")
    public CommonResult<Payment> create(@RequestBody Payment payment) {
        int rs = paymentService.create(payment);
        if (rs > 0)
            return new CommonResult(200, "插入成功,serverPort: "+serverPort, payment);
        else
            return new CommonResult(444, "插入失败", null);
    }

    @GetMapping("/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id) {
        System.out.println(id);
        Payment payment = paymentService.getPaymentById(id);
        if (payment != null)
            return new CommonResult(200, "查询成功,serverPort: "+serverPort, payment);
        else
            return new CommonResult(444, "没有查询到id="+id+"支付记录", null);
    }
}

消费者对接支付微服务(负载均衡)

改Consumer的Controller

修改处:public static final String PAYMENT_URL = “http://CLOUD-PAYMENT-SERVICE”;

@RestController
public class OrderTestPaymentClusterController {

    @Resource
    private RestTemplate restTemplate;
    // public static final String PAYMENT_URL = "http://localhost:8089";
    // CLOUD-PAYMENT-SERVICE为Payment微服务yml里面配置的spring.application.name=cloud-payment-service
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE"; 

    @PostMapping("/consumer/cluster/create")
    public CommonResult<Payment> create(Payment payment) {
        /**
         * restTemplate三个参数分别为URL、传入的参数、返回的结果类型
         */
        CommonResult commonResult = restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
        return commonResult;
    }

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

}

修改Consumer的Config.class(负载均衡)

修改处:给RestTemplate方法添加@LoadBalanced注解【不做负载均衡不要用此注解】

@Configuration
public class ApplicationContextConfig {

    /**
     * RestTemplate的作用是完成微服务之间的调用
     * @return
     */

    @Bean  //依赖注入
    @LoadBalanced //对payment8090和对payment8091负载均衡
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

效果

访问consumer接口,则调用payment8090和payment8091服务,表现在返货结果的端口号8090和8091交替出现

完善细节(修改Eureka注册中心显示名称及IP)

Discovery服务发现

含义:Eureka Client 可以发现所有注册在Eureka Server 里面的所有Eureka Client
下面以Eureka Client(Payment微服务)发现为例

修改主程序类

修改处:PaymentMain类上添加@EnableDiscoveryClient注解

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

编写Controller服务发现Discovery

    @Resource
    DiscoveryClient discoveryClient;
    
    @GetMapping("/payment/discovery")
    public List<Map<String, List<Map<String, Object>>>> getDiscovery(){
        List<Map<String, List<Map<String, Object>>>> servicesList = new ArrayList<>();
        List<String> services = discoveryClient.getServices();
        for (String service : services){
            Map<String, List<Map<String, Object>>> map = new HashMap<>();

            List<ServiceInstance> instances = discoveryClient.getInstances(service);

            List<Map<String, Object>> instanceDetail = new ArrayList<>();
            for (ServiceInstance instance : instances){
                Map<String,Object> mp = new HashMap<>();
                mp.put("instanceId", instance.getInstanceId());
                mp.put("instanceHost", instance.getHost());
                mp.put("instancePort", instance.getPort());
                mp.put("instanceUri", instance.getUri());
                instanceDetail.add(mp);
            }
            map.put(service, instanceDetail);
            servicesList.add(map);
        }
        return servicesList;
    }

运行结果

运行结果如下

[
    {
        "cloud-payment-service": [
            {
                "instancePort": 8091,
                "instanceUri": "http://192.168.112.1:8091",
                "instanceId": "payment8091",
                "instanceHost": "192.168.112.1"
            },
            {
                "instancePort": 8090,
                "instanceUri": "http://192.168.112.1:8090",
                "instanceId": "payment8090",
                "instanceHost": "192.168.112.1"
            },
            {
                "instancePort": 8089,
                "instanceUri": "http://192.168.112.1:8089",
                "instanceId": "192.168.112.1:cloud-payment-service:8089",
                "instanceHost": "192.168.112.1"
            }
        ]
    },
    {
        "cloud-consumer": [
            {
                "instancePort": 80,
                "instanceUri": "http://192.168.112.1:80",
                "instanceId": "192.168.112.1:cloud-consumer:80",
                "instanceHost": "192.168.112.1"
            }
        ]
    }
]

Eureka自我保护机制

自我保护机制的原因

为了防止Eureka Client可以正常运行,但是与Eureka Server网络不通的情况下,Eureka Server不会立刻将Eureka Client立刻从服务注册中心删掉

什么是自我保护模式

默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将注销实例(默认为90s),但是当网络分区故障发生(延迟、卡顿、拥挤)时,微服务与Eureka Server 之间无法正常通信,以上行为可能变得非常危险。因为微服务短时间内丢失过多的客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。

Eureka Server自我保护机制相关设置

eureka:
  server:
    # 关闭自我保护机制,保证不可用服务被及时踢除
    enable-self-preservation: false
    # 关闭自我保护机制,间隔时间设置
    eviction-interval-timer-in-ms: 2000

Eureka Client自我保护模式相关设置

eureka:
  instance:
    instance-id: payment8090   #修改名称
    prefer-ip-address: true
      #心跳检测与续约时间
      #开发时没置小些,保证服务关闭后注册中心能即使剔除服务
      #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
      lease-renewal-interval-in-seconds: 1
      #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
      lease-expiration-duration-in-seconds: 2

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.

现在一般用ZooKeeper代替

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值