Eureka簡介以及入門


1. Eureka簡介

1.什么是服務治理

Springcloud封裝了Netflix公司開發的Eureka來實現服務治理。

服務治理管理服務與服務之間的依賴關系,可以實現服務調用、負載均衡、容錯等,實現服務注冊於發現。

2.什么是服務注冊與發現

  Eureka采用CS架構。EurekaServer作為服務注冊的服務器,它是注冊中心。而系統中的其他服務,使用eureka的客戶端連接到EurekaServer並維持心跳連接。這樣系統的維護人員可以通過eurekaserver來監控系統各個服務的運行情況。

  在服務注冊發現中,有一個注冊中心。當服務器啟動時會把當前服務器自己的信息,比如服務地址、通訊地址等以別名方式注冊到注冊中心。另一方(消費者),以別名的方式到注冊中心獲取到實際的地址,然后再實現本地的服務調用。

3.Eureka的兩個組件

  EurekaServer提供注冊服務,各個微服務節點通過配置啟動后,會在eureka服務中進行注冊。這樣EurekaServer服務注冊表中將會存儲所有可調用服務節點的信息,服務節點的信息可以在界面中直觀的看到。

  EurekaClient通過注冊中心獲取服務。是一個Java客戶端,用於簡化EurekaServer的交互;具備一個內置的、使用輪詢(round-robin)負載算法的負載均衡器。在應用啟動后,將會向Eureka Server發送心跳(默認30s)。如果EurekaServer在多個心跳周期內沒有接收到某個節點的心跳,EurekaServer將會從注冊表中把這個服務節點移除(默認90s)。

2. Eureka基本使用

1. EurekaServer服務搭建-采用7001端口新建eureka單擊版Server

1.新建moudle

 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>cloud</artifactId>
        <groupId>cn.qz.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-eureka-server7001</artifactId>

    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--引入自己抽取的工具包-->
        <dependency>
            <groupId>cn.qz.cloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
</project>

3.編寫yml配置文件

server:
  port: 7001

eureka:
  instance:
    hostname: localhost #eureka服務端的實例名稱
  client:
    register-with-eureka: false     #false表示不向注冊中心注冊自己。
    fetch-registry: false     #false表示自己端就是注冊中心,我的職責就是維護服務實例,並不需要去檢索服務
    service-url:
    #單機就是7001自己
      defaultZone: http://localhost:7001/eureka/

4.編寫主啟動類(由於這個是作為Eureka的服務端,所以不需要其他業務類)

package cn.qz.cloud;

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

/**
 * @Author: qlq
 * @Description
 * @Date: 21:01 2020/10/2
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7001.class, args);
    }
}

5.啟動后訪問本機7001端口進行測試:

2. 服務 cloud-provider-payment8081注冊到eureka

1.pom文件增加如下EurekaClient客戶端

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

2.修改yml文件,增加eureka配置

eureka:
  client:
    #表示是否將自己注冊進EurekaServer默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊信息,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      defaultZone: http://localhost:7001/eureka

3.主啟動類增加如下注解

@EnableEurekaClient

4.啟動服務訪問eurekaserver的7001端口

注意需要先啟動eurekaserver端的7001端口。

 

   查看服務信息如圖,這里的服務名稱Application取的是服務注冊方的spring.application.name,所以這里的服務名稱一般要規范起名且不輕易修改。

3.訂單服務注冊餓到Eureka服務中心

1.pom文件增加如下客戶端配置:

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

2.修改yml配置,增加服務名稱和eureka配置

spring:
    application:
        name: cloud-order-service

eureka:
  client:
    #表示是否將自己注冊進EurekaServer默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊信息,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      defaultZone: http://localhost:7001/eureka

3.主配置類增加eureka注解

@EnableEurekaClient

4. 修改RestTemplate,增加負載均衡配置

package cn.qz.cloud.config;

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

/**
 * 注入一個bean
 */
@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

5.修改controller服務獲取地址

package cn.qz.cloud.controller;

import cn.qz.cloud.bean.Payment;
import cn.qz.cloud.utils.JSONResultUtil;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * @Author: qlq
 * @Description
 * @Date: 22:09 2020/9/25
 */
@RestController
@RequestMapping("/consumer")
public class OrderController {

    private static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    @PostMapping("/pay/save")
    public JSONResultUtil<Payment> save(@RequestBody Payment payment) {
        return restTemplate.postForObject(PAYMENT_URL + "/pay/save", payment, JSONResultUtil.class);
    }

    @GetMapping("/pay/listAll")
    public JSONResultUtil<List<Map<String, Object>>> listAll() {
        return restTemplate.getForObject(PAYMENT_URL + "/pay/listAll", JSONResultUtil.class);
    }
}

6.啟動服務測試即可:

$ curl http://localhost/consumer/pay/listAll
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:100   406    0   406    0     0   8638      0 --:--:-- --:--:-- --:--:-- 13096{"success":true,"code":"200","msg":"","data":[{"createtime":"2020-09-25T06:58:21.000+0000","serial":"測試","id":1},{"createtime":"2020-09-25T07:06:10.000+0000","serial":"測試","id":2},{"createtime":"2020-09-25T14:34:43.000+0000","serial":"測試1測試測試序列好23456","id":3},{"createtime":"2020-09-25T15:25:35.000+0000","serial":"測試1測試測試序列好23456555","id":1309514424784064514}]}

7.調用過程如下:

(1)啟動Eureka注冊中心

(2)啟動服務提供者Payment支付服務,支付服務會把自身信息(比如服務地址)以別名方式注冊到Eureka

(3)消費者Order調用接口時,實際是根據服務別名獲取到實際地址,然后yongHttpClient實現接調用

(4)消費者獲得服務信息后會緩存在JVM內存本地,默認30s更新一次服務調用地址

3.Eureka 集群

1.集群原理

  簡單說下,分布式環境有個CAP原則,又稱為CAP理論,主要思想是在任何一個分布式系統中都無法同時滿足CAP。

C(Consistency):表示一致性,所有的節點同一時間看到的是相同的數據。

A(Avaliablity):表示可用性,不管是否成功,確保一個請求都能接收到響應。

P(Partion Tolerance):分區容錯性,系統任意分區后,在網絡故障時,仍能操作。

  Eureka集群的原理是多台機器相互守望,互相關注,和zookeeper不同的是不存在主節點的概念。所以相比於Zookeeper的選舉主節點發生的處理中斷(CP),Eureka滿足的是AP原則(自我保護模式會保留異常的服務)。

2.集群搭建

2.1 搭建Eureka集群

0.配置兩個虛擬主機

修改C:\Windows\System32\drivers\etc\hosts文件:

127.0.0.1       eureka1.com
127.0.0.1       eureka2.com

1.新建cloud-eureka-server7002與7001服務組成集群

 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>cloud</artifactId>
        <groupId>cn.qz.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-eureka-server7002</artifactId>

    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--引入自己抽取的工具包-->
        <dependency>
            <groupId>cn.qz.cloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
</project>

3.修改yml配置

server:
  port: 7002

eureka:
  instance:
    hostname: eureka2.com #eureka服務端的實例名稱
  client:
    register-with-eureka: false     #false表示不向注冊中心注冊自己。
    fetch-registry: false     #false表示自己端就是注冊中心,我的職責就是維護服務實例,並不需要去檢索服務
    service-url:
    #單機就是7001自己
      #defaultZone: http://localhost:7001/eureka/
    #集群指向其它eureka
      defaultZone: http://eureka1.com:7001/eureka/

4.新建啟動類:

package cn.qz.cloud;

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

/**
 * @Author: qlq
 * @Description
 * @Date: 9:07 2020/10/3
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7002 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7002.class, args);
    }
}

5.修改原來7001Server的配置

server:
  port: 7001

eureka:
  instance:
    hostname: eureka1.com #eureka服務端的實例名稱
  client:
    register-with-eureka: false     #false表示不向注冊中心注冊自己。
    fetch-registry: false     #false表示自己端就是注冊中心,我的職責就是維護服務實例,並不需要去檢索服務
    service-url:
    #單機就是7001自己
      #defaultZone: http://localhost:7001/eureka/
    #集群指向其它eureka
      defaultZone: http://eureka2.com:7002/eureka/

6.啟動7001、7002服務查看控制台:(可以看到集群信息搭建完成)

 2.2 payment支付服務集群

1.新建模塊cloud-provider-payment8082

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>cloud</artifactId>
        <groupId>cn.qz.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8082</artifactId>

    <dependencies>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--引入自己抽取的工具包-->
        <dependency>
            <groupId>cn.qz.cloud</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>
        <!-- spring-boot整合mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</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>
        <!-- springdata jpa依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

3修改yml

server:
  port: 8082

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 當前數據源操作類型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驅動包
    url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
  jpa:
    showSql: true
    hibernate.ddlAuto: update
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

mybatis-plus:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: cn.qz.cloud.bean    # 所有Entity別名類所在包

eureka:
  client:
    #表示是否將自己注冊進EurekaServer默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊信息,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      #defaultZone: http://localhost.com:7001/eureka
      # 集群版
      defaultZone: http://eureka1.com:7001/eureka,http://eureka2.com:7002/eureka

4.入口類以及業務類同cloud-provider-payment8081

5.兩個服務增加一個接口:(getServerPort)

package cn.qz.cloud.controller;

import cn.qz.cloud.bean.Payment;
import cn.qz.cloud.service.PaymentService;
import cn.qz.cloud.utils.JSONResultUtil;
import com.baomidou.mybatisplus.service.IService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: qlq
 * @Description
 * @Date: 14:49 2020/9/25
 */
@RestController
@RequestMapping("/pay")
public class PaymentController extends AbstractController<Payment, Long> {

    @Autowired
    private PaymentService service;

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

    @Override
    public IService<Payment> getBaseService() {
        return service;
    }

    @GetMapping("/getServerPort")
    public JSONResultUtil<String> getServerPort() {
        return JSONResultUtil.successWithData(serverPort);
    }
}

2.3. 訂單模塊修改

1.修改yml

eureka:
  client:
    #表示是否將自己注冊進EurekaServer默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊信息,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      #defaultZone: http://localhost:7001/eureka
      # 集群版
      defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka

2.增加接口

package cn.qz.cloud.controller;

import cn.qz.cloud.bean.Payment;
import cn.qz.cloud.utils.JSONResultUtil;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * @Author: qlq
 * @Description
 * @Date: 22:09 2020/9/25
 */
@RestController
@RequestMapping("/consumer")
public class OrderController {

    private static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    @PostMapping("/pay/save")
    public JSONResultUtil<Payment> save(@RequestBody Payment payment) {
        return restTemplate.postForObject(PAYMENT_URL + "/pay/save", payment, JSONResultUtil.class);
    }

    @GetMapping("/pay/listAll")
    public JSONResultUtil<List<Map<String, Object>>> listAll() {
        return restTemplate.getForObject(PAYMENT_URL + "/pay/listAll", JSONResultUtil.class);
    }

    @GetMapping("/pay/getServerPort")
    public JSONResultUtil<String> getServerPort() {
        return restTemplate.getForObject(PAYMENT_URL + "/pay/getServerPort", JSONResultUtil.class);
    }
}

3.啟動服務:

依次啟動eurekaserver7001、70002、payment8081、8082、訂單80

4.訪問eureka服務查看:

 5.訪問訂單服務的http://localhost/consumer/getServerPort地址

可以看到是8081、8082端口輪詢。

4.Eureka補充

1.actuator完善信息

存在的兩個問題:

1. 服務名稱顯示主機名稱,不規范

2.服務的地址信息沒有顯示

鼠標懸浮的時候沒有顯示IP地址,我們希望顯示IP地址。

解決辦法:application.yml修改如下配置

eureka:
  client:
    #表示是否將自己注冊進EurekaServer默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊信息,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      #defaultZone: http://localhost:7001/eureka
      # 集群版
      defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka
  instance:
      instance-id: payment8081
      #訪問路徑可以顯示IP地址
      prefer-ip-address: true

 

查看效果如下:

2. 服務發現discovery

 DiscoveryClient 可以獲取很多的服務信息,包括服務名稱以及服務實例信息ServiceInstance。如下:

@RestController
@RequestMapping("/pay")
@Slf4j
public class PaymentController extends AbstractController<Payment, Long> {

    @Autowired
    private PaymentService service;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/discovery")
    public JSONResultUtil<List<String>> discovery() {
        List<String> services =  discoveryClient.getServices();
        log.info("services: {}", services);

        List<ServiceInstance> serviceInstances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance serviceInstance :serviceInstances ) {
            log.info("InstanceId: {}, Host: {}, Port: {}", serviceInstance.getInstanceId(),serviceInstance.getHost(), serviceInstance.getPort());
        }

        return JSONResultUtil.successWithData(services);
    }
}

 查看打印的日志如下:

2020-10-03 22:37:28.968 INFO 7892 --- [nio-8082-exec-1] c.qz.cloud.controller.PaymentController : services: [cloud-payment-service]
2020-10-03 22:37:28.980 INFO 7892 --- [nio-8082-exec-1] c.qz.cloud.controller.PaymentController : InstanceId: payment8081, Host: 192.168.1.36, Port: 8081
2020-10-03 22:37:28.981 INFO 7892 --- [nio-8082-exec-1] c.qz.cloud.controller.PaymentController : InstanceId: payment8082, Host: 192.168.1.36, Port: 8082

補充:Eureka服務發現實際過程

  在Eureka環境中,discoverClient實際上是org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient,其注入過程在:org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration#discoveryClient

    @Bean
    @ConditionalOnMissingBean
    public EurekaDiscoveryClient discoveryClient(EurekaClient client,
            EurekaClientConfig clientConfig) {
        return new EurekaDiscoveryClient(client, clientConfig);
    }

  EurekaDiscoveryClientConfiguration 配置類會在引入包的時候自動加載,是在spring-cloud-netflix-eureka-client-2.2.1.RELEASE.jar!\META-INF\spring.factories 配置的

查看org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient#getServices:

    @Override
    public List<String> getServices() {
        Applications applications = this.eurekaClient.getApplications();
        if (applications == null) {
            return Collections.emptyList();
        }
        List<Application> registered = applications.getRegisteredApplications();
        List<String> names = new ArrayList<>();
        for (Application app : registered) {
            if (app.getInstances().isEmpty()) {
                continue;
            }
            names.add(app.getName().toLowerCase());

        }
        return names;
    }

  可以看到是獲取緩存在com.netflix.discovery.shared.Applications#applications  的信息。這個信息是通過scheduler任務調度器去定時拿取的。

 

3.Eureka自我保護機制和禁止自我保護

1.自我保護機制

  總結一句話就是:某一時刻某個服務不可用了,Eureka不會立即清掉服務,依舊會對該服務的信息進行保存。

官方解釋:自我保護模式正是一種針對網絡異常波動的安全保護措施,使用自我保護模式能使Eureka集群更加的健壯、穩定的運行。

如果在15分鍾內超過85%的客戶端節點都沒有正常的心跳,那么Eureka就認為客戶端與注冊中心出現了網絡故障,Eureka Server自動進入自我保護機制,此時會出現以下幾種情況:

Eureka Server不再從注冊列表中移除因為長時間沒收到心跳而應該過期的服務。

Eureka Server仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其它節點上,保證當前節點依然可用。

當網絡穩定時,當前Eureka Server新的注冊信息會被同步到其它節點中。

 2.關閉自我保護

  在自我保護模式開啟的情況下,當服務掛掉之后,eureka中不會刪掉服務,消費者仍然會調用該服務接口造成連接超時異常。

  開發環境的話建議關掉自我保護,生產環境開啟自我保護。

關閉自我保護之后從Eureka查看,從web查看提示如下:

1. Eureka服務端配置如下:

eureka:
  instance:
    hostname: eureka1.com #eureka服務端的實例名稱
  client:
    register-with-eureka: false     #false表示不向注冊中心注冊自己。
    fetch-registry: false     #false表示自己端就是注冊中心,我的職責就是維護服務實例,並不需要去檢索服務
    service-url:
    #單機就是7001自己
      #defaultZone: http://localhost:7001/eureka/
    #集群指向其它eureka
      defaultZone: http://eureka2.com:7002/eureka/
  server:
    #服務端是否開啟自我保護機制 (默認true)
    enable-self-preservation: false
    #掃描失效服務的間隔時間(單位毫秒,默認是60*1000)即60秒
    eviction-interval-timer-in-ms: 2000

 2.Eureka客戶端如下:

eureka:
  client:
    #表示是否將自己注冊進EurekaServer默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊信息,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      #defaultZone: http://localhost:7001/eureka
      # 集群版
      defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka
  instance:
      instance-id: payment8081
      #訪問路徑可以顯示IP地址
      prefer-ip-address: true
      # 客戶端向注冊中心發送心跳的時間間隔,(默認30秒)
      lease-renewal-interval-in-seconds: 1
      #Eureka注冊中心(服務端)在收到客戶端心跳之后,等待下一次心跳的超時時間,如果在這個時間內沒有收到下次心跳,則移除該客戶端。(默認90秒)
      lease-expiration-duration-in-seconds: 2

 

補充: restTemplate 有兩種方法,xxxForObject和xxxForEntity

  ForObject直接返回結果,ForEntity會返回詳細的響應頭、狀態碼等信息。如下:

        ResponseEntity<JSONResultUtil> entity = restTemplate.getForEntity(PAYMENT_URL + "/pay/getServerPort", JSONResultUtil.class);
        HttpStatus status = entity.getStatusCode();
        JSONResultUtil<String> result = entity.getBody();
        HttpHeaders headers = entity.getHeaders();

補充: eureka可以自定義元數據

例如:

1.  application.yml增加元數據配置:

eureka:
  client:
    #表示是否將自己注冊進EurekaServer默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊信息,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      defaultZone: http://localhost:7001/eureka
      # 集群版
#      defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka
  instance:
      instance-id: payment8081
      #訪問路徑可以顯示IP地址
      prefer-ip-address: true
      metadata-map:
        key1: value1
        key2: value2

元數據的值也可以用${anotherKey}獲取另一個key的值。這時候啟動服務,我們可以查看eureka管理界面,如下:

$ curl http://localhost:7001/eureka/apps/CLOUD-PAYMENT-SERVICE
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1642    0  1642    0     0  17655      0 --:--:-- --:--:-- --:--:-- 34936<application>
  <name>CLOUD-PAYMENT-SERVICE</name>
  <instance>
    <instanceId>payment8081</instanceId>
    <hostName>192.168.1.6</hostName>
    <app>CLOUD-PAYMENT-SERVICE</app>
    <ipAddr>192.168.1.6</ipAddr>
    <status>UP</status>
    <overriddenstatus>UNKNOWN</overriddenstatus>
    <port enabled="true">8081</port>
    <securePort enabled="false">443</securePort>
    <countryId>1</countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwn</name>
    </dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30</renewalIntervalInSecs>
      <durationInSecs>90</durationInSecs>
      <registrationTimestamp>1604827931637</registrationTimestamp>
      <lastRenewalTimestamp>1604828381782</lastRenewalTimestamp>
      <evictionTimestamp>0</evictionTimestamp>
      <serviceUpTimestamp>1604827931109</serviceUpTimestamp>
    </leaseInfo>
    <metadata>
      <key1>value1</key1>
      <key2>value2</key2>
      <management.port>8081</management.port>
      <jmx.port>64188</jmx.port>
    </metadata>
    <homePageUrl>http://192.168.1.6:8081/</homePageUrl>
    <statusPageUrl>http://192.168.1.6:8081/actuator/info</statusPageUrl>
    <healthCheckUrl>http://192.168.1.6:8081/actuator/health</healthCheckUrl>
    <vipAddress>cloud-payment-service</vipAddress>
    <secureVipAddress>cloud-payment-service</secureVipAddress>
    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1604827931637</lastUpdatedTimestamp>
    <lastDirtyTimestamp>1604827931099</lastDirtyTimestamp>
    <actionType>ADDED</actionType>
  </instance>
</application>

http://ip:port/eureka/apps 可以查看所有服務信息,http://ip:port/eureka/apps/服務名稱  可以查看單個服務信息。

2. Controller 用discoveryClient獲取服務信息,包括元數據信息

    @GetMapping("/serviceInstanceInfos")
    public JSONResultUtil<List<ServiceInstance>> serviceInstanceInfos() {
        List<String> services =  discoveryClient.getServices();
        log.info("services: {}", services);

        List<ServiceInstance> serviceInstances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        return JSONResultUtil.successWithData(serviceInstances);
    }

結果:

{
    "success": true,
    "code": "200",
    "msg": "",
    "data": [
        {
            "host": "192.168.1.6",
            "port": 8081,
            "metadata": {
                "key1": "value1",
                "key2": "value2",
                "management.port": "8081",
                "jmx.port": "60098"
            },
            "secure": false,
            "uri": "http://192.168.1.6:8081",
            "serviceId": "CLOUD-PAYMENT-SERVICE",
            "instanceId": "payment8081",
            "instanceInfo": {
                "instanceId": "payment8081",
                "app": "CLOUD-PAYMENT-SERVICE",
                "appGroupName": null,
                "ipAddr": "192.168.1.6",
                "sid": "na",
                "homePageUrl": "http://192.168.1.6:8081/",
                "statusPageUrl": "http://192.168.1.6:8081/actuator/info",
                "healthCheckUrl": "http://192.168.1.6:8081/actuator/health",
                "secureHealthCheckUrl": null,
                "vipAddress": "cloud-payment-service",
                "secureVipAddress": "cloud-payment-service",
                "countryId": 1,
                "dataCenterInfo": {
                    "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                    "name": "MyOwn"
                },
                "hostName": "192.168.1.6",
                "status": "UP",
                "overriddenStatus": "UNKNOWN",
                "leaseInfo": {
                    "renewalIntervalInSecs": 30,
                    "durationInSecs": 90,
                    "registrationTimestamp": 1604828867711,
                    "lastRenewalTimestamp": 1604828867711,
                    "evictionTimestamp": 0,
                    "serviceUpTimestamp": 1604828682411
                },
                "isCoordinatingDiscoveryServer": false,
                "metadata": {
                    "key1": "value1",
                    "key2": "value2",
                    "management.port": "8081",
                    "jmx.port": "60098"
                },
                "lastUpdatedTimestamp": 1604828867711,
                "lastDirtyTimestamp": 1604828867030,
                "actionType": "ADDED",
                "asgName": null
            },
            "scheme": null
        }
    ]
}

 補充:application.properties 和 yml文件可以使用${}取其他變量

比如:testVal=${testVal2:this is default} 是取變量testVal2的值,取不到就是冒號后面的默認值;如果testVal2有值就取testVal2的值。

補充:yml和properties文件也可以獲取maven中的屬性

(1)pom中增加屬性

    <properties>
        <key3pom>value3</key3pom>
    </properties>

build 標簽增加如下屬性:

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <!--開啟過濾,用指定的參數替換directory下的文件中的參數-->
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

(2) yml中使用@var@ 獲取pom中屬性

server:
  port: 8081

spring:
  application:
    name: cloud-payment-service
#    sleuth鏈路追蹤
#  zipkin:
#    base-url: http://localhost:9411
#  sleuth:
#    sampler:
      #采樣取值介於 0到1之間,1則表示全部收集
#      probability: 1
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 當前數據源操作類型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驅動包
    url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
  jpa:
    showSql: true
    hibernate.ddlAuto: update
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

mybatis-plus:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: cn.qz.cloud.bean    # 所有Entity別名類所在包

eureka:
  client:
    #表示是否將自己注冊進EurekaServer默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊信息,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      defaultZone: http://localhost:7001/eureka
      # 集群版
#      defaultZone: http://eureka1.com:7001/eureka, http://eureka2.com:7002/eureka
  instance:
      instance-id: payment8081
      #訪問路徑可以顯示IP地址
      prefer-ip-address: true
      metadata-map:
        key1: value1
        key3: @key3pom@

# 讀取pom.xml 聲明的屬性
key3: @key3pom@

(3)Controller 測試

    @Value("${key3}")
    private String key3;

    @GetMapping("/keys")
    public JSONResultUtil<String> keys() {
        return JSONResultUtil.successWithData(key3);
    }

測試:

$ curl http://root:8081/pay/keys
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- 100    54    0    54    0     0   1148      0 --:--:-- --:--:-- --:--:--  3375{"success":true,"code":"200","msg":"","data":"value3"}

(4) 查看元數據

補充:局域網內部可以通過主機名來進行通信 

  今天發現局域網內部可以通過主機名稱來進行通信。局域網內部ping的時候也可以直接ping主機。例如:

$ curl http://root:8081/pay/discovery
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- 100    71    0    71    0     0   1543      0 --:--:-- --:--:-- --:--:--  4733{"success":true,"code":"200","msg":"","data":["cloud-payment-service"]}

 

如上,root是計算機名稱。並且沒有在hosts內配置域名映射。

關於其大致原理可以參考:https://wenku.baidu.com/view/95af2d48a76e58fafab003a8.html

補充:eureka 訪問信息並且返回JSON格式的數據:

eureka 服務端是以Jersey 接受客戶端請求,默認返回的是XML格式的數據,也可以返回JOSN的數據

查看所有服務信息:    curl --header 'Accept: application/json' http://localhost:7001/eureka/apps
查看單個服務的信息:    curl --header 'Accept: application/json' http://localhost:7001/eureka/apps/CLOUD-PROVIDER-HYSTRIX-PAYMENT

 

 補充:Eureka 整合SpringSecurity實現登陸時候權限校驗

  在普通的開發過程中Eureka 可以直接訪問到,不需要增加賬號、密碼;但是有時候為了安全Eureka 需要增加密碼機制。Eureka 可以集成SpringSecurity實現賬號機制

1.  引入security 相關依賴

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

2. application.yml 配置security 相關配置

spring:
  security:
    user:
      name: eureka
      password: 123456

3. 增加配置類(這個配置類必須有,否則客戶端使用賬號密碼也注冊不上來)

package cn.qz.cloud.configuration;


import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        super.configure(http);
    }
}

4. 接下來正常訪問Eureka  即可,Eureka 會重定向到登錄界面 http://localhost:7001/login, 然后正常登錄即可

5. Eureka 客戶端注冊到Eureka 需要驗證賬號密碼

defaultZone: http://eureka:123456@localhost:7001/eureka

6. curl訪問的時候增加賬號、密碼

curl --header 'Accept: application/json' http://eureka:123456@localhost:7001/eureka/apps

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM