初学eureka注册中心

Posted java小dom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初学eureka注册中心相关的知识,希望对你有一定的参考价值。

在学习Eureka之前我们要先了解他是什么,可以帮我们完成哪些功能

1.什么是注册中心Eureka

        EurekaNetflix开发的服务发现组件,本身是一个基于REST(一种软件架构的风格)的服务。Spring Cloud把他集成在其子项目spring-cloud-netflix中,以实现Spring Cloud的服务注册与发现,同时还提供了负载均衡、故障转移等能力
 

2.Eureka三大角色

       1.EurekaServer :注册中心

              通过 RegisterGetRenew 等接口提供服务的注册和发现。

        2.ServiceProvider: 提供方

                 把自身的服务实例注册到 EurekaServer

        3.ServiceConsumer 服务调用方

              通过Eureka Server 获取服务列表,消费服务

                

 

 

 


 

2.Eureka入门

     1.创建Eureka注册中心

           1.创建工程

                            springcloud_eureka_server

                              2.pom.xml

                                   <?xmlversion="1.0" encoding="UTF-8"?>

<projectxmlns="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.0http://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>

   <parent>

       <groupId>org.springframework.boot</groupId>

       <artifactId>spring-boot-starter-parent</artifactId>

       <version>2.1.6.RELEASE</version>

   </parent>

 

   <groupId>com.usian</groupId>

   <artifactId>springcloud_eureka_server</artifactId>

   <version>1.0-SNAPSHOT</version>

 

   <properties>

       <java.version>1.8</java.version>

   </properties>

   <dependencyManagement>

       <dependencies>

           <dependency>

               <groupId>org.springframework.cloud</groupId>

               <artifactId>spring-cloud-dependencies</artifactId>

               <version>Greenwich.SR2</version>

                <type>pom</type>

               <scope>import</scope>

           </dependency>

       </dependencies>

   </dependencyManagement>

   <dependencies>

       <!-- springBoot的启动器 -->

       <dependency>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-web</artifactId>

       </dependency>

       <!--eureka-server服务端 -->

       <dependency>

           <groupId>org.springframework.cloud</groupId>

           <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>

       </dependency>

   </dependencies>

</project>

3.创建启动类

 

packagecom.usian;

 

importorg.springframework.boot.SpringApplication;

importorg.springframework.boot.autoconfigure.SpringBootApplication;

importorg.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

 

@EnableEurekaServer//EurekaServer服务器端启动类,接受其他微服务注册进来

@SpringBootApplication

public classEurekaApp {

     public static void main(String[] args) {

          SpringApplication.run(EurekaApp.class,args);

     }

}

4.配置文件

server.port=8761

 

#设置eureka不注册自己

eureka.client.register-with-eureka=false

#设置eureka不获取其他服务的注册信息

eureka.client.fetch-registry=false

4.测试

       浏览器访问:http://127.0.0.1:8761/


4.3.2.创建common_pojo

4.3.2.1.创建工程

springcloud_common_pojo

4.3.2.2.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">


   <modelVersion>4.0.0</modelVersion>


   <groupId>com.usian</groupId>


   <artifactId>springcloud_common_pojo</artifactId>


   <version>1.0-SNAPSHOT</version>
</project>

4.3.2.3.pojo

package com.usian.pojo;

public class User {

   private Integer id;

   private String nam;

   private Integer age;

   public User() {

  }

   public User(Integer id, String nam, Integer age) {

       this.id = id;

       this.nam = nam;

       this.age = age;

  }

   public Integer getId() {

       return id;

  }

   public void setId(Integer id) {

       this.id = id;

  }

   public String getNam() {

       return nam;

  }

   public void setNam(String nam) {

       this.nam = nam;

  }

   public Integer getAge() {

       return age;

  }

   public void setAge(Integer age) {

       this.age = age;

  }

}

4.3.3.创建服务提供者

4.3.3.1.创建工程

springcloud_eureka_provider

4.3.3.2.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">


   <modelVersion>4.0.0</modelVersion>


   <parent>


       <groupId>org.springframework.boot</groupId>


       <artifactId>spring-boot-starter-parent</artifactId>


       <version>2.1.6.RELEASE</version>


   </parent>


   <groupId>com.usian</groupId>


   <artifactId>springcloud_eureka_provider</artifactId>


   <version>1.0-SNAPSHOT</version>


   <properties>


       <java.version>1.8</java.version>


   </properties>


   <dependencyManagement>


       <dependencies>


           <dependency>


               <groupId>org.springframework.cloud</groupId>


               <artifactId>spring-cloud-dependencies</artifactId>


               <version>Greenwich.SR2</version>


               <type>pom</type>


               <scope>import</scope>


           </dependency>


       </dependencies>


   </dependencyManagement>


   <dependencies>


       <!-- springBoot的启动器 -->


       <dependency>


           <groupId>org.springframework.boot</groupId>


           <artifactId>spring-boot-starter-web</artifactId>


       </dependency>


       <!--eureka-server客户端 -->


       <dependency>


           <groupId>org.springframework.cloud</groupId>


           <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>


       </dependency>


       <dependency>


           <groupId>com.usian</groupId>


           <artifactId>springcloud_common_pojo</artifactId>


           <version>1.0-SNAPSHOT</version>


       </dependency>


   </dependencies>
</project>

4.3.3.3.App

@EnableEurekaClient//允许向服务端注册服务@SpringBootApplicationpublic class ProviderApp {

public static void main(String[] args) {

SpringApplication.run(ProviderApp.class, args);

}

}

4.3.3.4.application.properties

#向注册中心注册的名字spring.application.name=eureka-providerserver.port=8777#设置注册中心的地址eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8761/eureka/

4.3.3.5.service

package com.usian.service;

import com.usian.pojo.User;

import org.springframework.stereotype.Service;

@Servicepublic class UserServiceImpl implements UserService {

@Overridepublic User getUser(Integer id) {

return new User(id,"王粪堆",18);

}

}

4.3.3.6.controller

package com.usian.controller;

import com.usian.pojo.User;

import com.usian.service.UserService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

@RestControllerpublic class ProviderController {



@Autowiredprivate UserService userService;

@RequestMapping("user/{id}")

public User getUser(@PathVariable Integer id){

return userService.getUser(id);

}

}

4.3.3.7.测试

4.3.4.创建服务消费者

4.3.4.1.创建工程

springcloud_eureka_consumer

4.3.4.2.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">


   <modelVersion>4.0.0</modelVersion>


   <parent>


       <groupId>org.springframework.boot</groupId>


       <artifactId>spring-boot-starter-parent</artifactId>


       <version>2.1.6.RELEASE</version>


   </parent>


   <groupId>com.usian</groupId>


   <artifactId>springcloud_eureka_consumer</artifactId>


   <version>1.0-SNAPSHOT</version>


   <properties>


       <java.version>1.8</java.version>


   </properties>


   <dependencyManagement>


       <dependencies>


           <dependency>


               <groupId>org.springframework.cloud</groupId>


               <artifactId>spring-cloud-dependencies</artifactId>


               <version>Greenwich.SR2</version>


               <type>pom</type>


               <scope>import</scope>


           </dependency>


       </dependencies>


   </dependencyManagement>


   <dependencies>


       <!-- springBoot的启动器 -->


       <dependency>


           <groupId>org.springframework.boot</groupId>


           <artifactId>spring-boot-starter-web</artifactId>


       </dependency>


       <!--eureka-server客户端 -->


       <dependency>


           <groupId>org.springframework.cloud</groupId>


           <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>


       </dependency>


       <dependency>


           <groupId>com.usian</groupId>


           <artifactId>springcloud_common_pojo</artifactId>


           <version>1.0-SNAPSHOT</version>


       </dependency>


   </dependencies>
</project>

4.3.4.3.App

package com.usian;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

//允许向注册中心注册该服务,并可以获取其他服务的调用地址@EnableEurekaClient@SpringBootApplicationpublic class ConsumerApp {

   public static void main(String[] args) {

       SpringApplication.run(ConsumerApp.class, args);

  }

}

4.3.4.4.application.properties

#注册中心挂个名字spring.application.name=eureka-consumerserver.port=8666#设置服务注册中心地址eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8761/eureka/

4.3.4.5.config

package com.usian.config;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.client.RestTemplate;

@Configurationpublic class ConfigBean {

@Beanpublic RestTemplate restTemplate(){

return new RestTemplate();

}

}

4.3.4.6.controller

RestTemplateSpring提供的用于访问Rest服务的客户端,RestTemplatehttpClient类似,都是java中可以模拟http请求的封装,但是RestTemplatehttpClient更优雅,它是spring中的一个封装功能。

package com.usian.controller;

import com.usian.pojo.User;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cloud.client.ServiceInstance;

import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.client.RestTemplate;

@RestControllerpublic class UserController {



//eureka注册中心获取服务端的ip、端口、要调用的服务@Autowiredprivate LoadBalancerClient loadBalancerClient;

   

//访问Rest服务的客户端@Autowiredprivate RestTemplate restTemplate;

@RequestMapping(value="consumer/user/{id}",method= RequestMethod.GET)

public User getUserBy(@PathVariable Integer id){

ServiceInstance si = loadBalancerClient.choose("eureka-provider");

//指定要调用的服务String url = "http://"+si.getHost()+":"+si.getPort()+"/user/"+id;

return restTemplate.getForObject(url, User.class);

}

}

4.3.4.7.测试

     

4.4.集群版的Eureka注册中心

4.4.1.创建工程

springcloud_eureka_server_cluster

4.4.2.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">


   <modelVersion>4.0.0</modelVersion>


   <parent>


       <groupId>org.springframework.boot</groupId>


       <artifactId>spring-boot-starter-parent</artifactId>


       <version>2.1.6.RELEASE</version>


   </parent>


   <groupId>com.usian</groupId>


   <artifactId>springcloud_eureka_server_cluster</artifactId>


   <version>1.0-SNAPSHOT</version>


   <properties>


       <java.version>1.8</java.version>


   </properties>


   <dependencyManagement>


       <dependencies>


           <dependency>


               <groupId>org.springframework.cloud</groupId>


               <artifactId>spring-cloud-dependencies</artifactId>


               <version>Greenwich.SR2</version>


               <type>pom</type>


               <scope>import</scope>


           </dependency>


       </dependencies>


   </dependencyManagement>


   <dependencies>


       <!-- springBoot的启动器 -->


       <dependency>


           <groupId>org.springframework.boot</groupId>


           <artifactId>spring-boot-starter-web</artifactId>


       </dependency>


       <!--eureka-server服务端 -->


       <dependency>


           <groupId>org.springframework.cloud</groupId>


           <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>


       </dependency>


   </dependencies>


   <build>


       <plugins>


           <plugin>


               <!-- 打包插件 -->


               <groupId>org.springframework.boot</groupId>


               <artifactId>spring-boot-maven-plugin</artifactId>


           </plugin>


       </plugins>


   </build>
</project>

4.4.3.App

package com.usian;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer//EurekaServer服务器端启动类,接受其他微服务注册进来@SpringBootApplicationpublic class EurekaApp {

public static void main(String[] args) {

SpringApplication.run(EurekaApp.class, args);

}

}

4.4.4.application.properties

application-eureka1.properties

spring.application.name=eureka-serverserver.port=8761#设置服务注册中心地址,指向另一个注册中心eureka.client.serviceUrl.defaultZone=http://eureka2:8761/eureka/

application-eureka2.properties

spring.application.name=eureka-serverserver.port=8761#设置服务注册中心地址,指向另一个注册中心eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/

4.4.5.logback.xml

<?xml version="1.0" encoding="UTF-8" ?><configuration><!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->  

   <property name="LOG_HOME" value="${catalina.base}/logs/" />  

   <!-- 控制台输出 -->  

   <appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">

      <!-- 日志输出编码 -->  

       <layout class="ch.qos.logback.classic.PatternLayout">  

            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->

           <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n  

           </pattern>  

       </layout>  

   </appender>  

   <!-- 按照每天生成日志文件 -->  

   <appender name="RollingFile"  class="ch.qos.logback.core.rolling.RollingFileAppender">  

       <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">


           <!--日志文件输出的文件名-->


           <FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern>  

           <MaxHistory>30</MaxHistory>


       </rollingPolicy>  

       <layout class="ch.qos.logback.classic.PatternLayout">  

           <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->

           <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n  

           </pattern>  

      </layout>

       <!--日志文件最大的大小-->


      <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">


        <MaxFileSize>10MB</MaxFileSize>


      </triggeringPolicy>


   </appender>    

   <!-- 日志输出级别 -->


   <root level="DEBUG">  

       <appender-ref ref="Stdout" />  

       <appender-ref ref="RollingFile" />  

   </root>

<!--日志异步到数据库 -->  

<!--     <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">

       日志异步到数据库


       <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">


          连接池


          <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">


             <driverClass>com.mysql.jdbc.Driver</driverClass>


             <url>jdbc:mysql://127.0.0.1:3306/databaseName</url>


             <user>root</user>


             <password>root</password>


           </dataSource>


       </connectionSource>


 </appender> -->
</configuration>

4.4.6.Eureka集群部署

部署环境:需要安装jdk1.8,正确配置环境变量。

注意:需要关闭 linux的防火墙,或者是开放8761 端口

4.4.6.1.将项目打jar

     

4.4.6.2 上传jar包到服务器

/usr/local/创建一个 eureka 的目录

将项目的jar 包拷贝到/usr/local/eureka

4.4.6.3.编写启动脚本文件

     

     

4.4.6.4.修改 linuxhost

Vim/etc/hosts

192.168.202.130 eureka1192.168.202.131 eureka2

4.4.6.5.启动eureka注册中心

./server.sh start #启动

./server.sh stop #停止

4.4.6.6.测试

浏览器访问:http://192.168.80.140:8761/

     

4.4.6.7.向集群版Eureka发布服务

4.4.6.7.1.修改windowshost文件
192.168.202.130 eureka1192.168.202.131 eureka2
4.4.5.7.2.创建provider配置文件
spring.application.name=eureka-providerserver.port=8777#设置服务注册中心地址,指向另一个注册中心eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/,http://eureka2:8761/eureka
4.4.6.7.3.修改Consumer配置文件
spring.application.name=eureka-consumerserver.port=8761#设置服务注册中心地址,指向另一个注册中心eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eureka/,http://eureka2:8761/eureka

4.6.7.测试

1、开启两台服务器以及运行服务器中的server.sh

2、运行服务提供者工程

3、运行服务消费者工程

4、测试

     

5.Eureka集群原理

Register(服务注册):把自己的 IP 和端口注册给 Eureka

Renew(服务续约):发送心跳包,每 30 秒发送一次。告诉 Eureka 自己还活着。


Cancel(服务下线):当 provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除。防止consumer调用到


              不存在的服务。


Get Registry(获取服务注册列表):获取服务列表。


Replicate(集群中数据同步)eureka 集群中的数据复制与同步。


Make Remote Call(远程调用):完成服务的远程调用。

6.EurekaZookeeper的区别

6.1.CAP原则

CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、Availability (可用性)、Partition tolerance (分区容错性),三者不可兼得。

CAP Eric Brewer 2000 PODC会议上提出。该猜想在提出两年后被证明成立,成为我们熟知的 CAP定理

     

一致性和可用性,为什么不可能同时成立?

答案很简单,因为G1G2可能通信失败(即出现分区容错)。

如果保证 G2 的一致性,那么 G1 必须在写操作时,锁定 G2 的读操作和写操作。只有数据同步后,才能重新开放读写。锁定期间,G2 不能读写,可用性不成立。

如果保证 G2 的可用性,那么势必不能锁定 G2,一致性不成立。

6.2.EurekaZookeeper好在哪里?

     

·      zookeeperzookeeper的写操作由leader负责,分区部署时失去leader概率非常大。leader死了或连不上了,就要重新选举且时间是30~120scp),且选举期间整个zk集群是都是不可用的

·      eurekaEureka各个节点都是平等的,几个节点挂掉不影响正常节点的工作,只不过查到的信息可能和其他节点不一样(ap

7.Eureka的自我保护

7.1.什么是自我保护?

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

     

保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会保护超过 60 秒没有发送心跳服务,不再删除服务注册表中的数据。

1,自我保护的条件

微服务在 Eureka 上注册后,会每 30 秒发送心跳包,每分钟 2 个心跳的固定频率因子

自我保护模式被激活的条件是:在 1 分钟后,Renews (lastmin) < Renews threshold

2,有两种情况会导致 EurekaServer 收不到微服务的心跳

a.微服务的自身的故障关闭:只会导致个别服务出现故障,一般不会出现大面积故障

b.网络故障:通常会导致 Eureka Server 在短时间内无法收到大批心跳。考虑到这个原因,Eureka 设置了一

个阀值,当判断挂掉的服务的数量超过阀值时,Eureka Server 认为很大程度上出现了网络故障,将不再删

除心跳过期的服务。

3,自我保护的机制(阀值)

EurekaServer 在运行期间,会统计心跳失败的比例是否低于85% 

1、低于85%:不在从注册列表中移除服务(网络故障)

2、不低于85%:删除超过90秒没有发送心跳的服务(非网络故障)


  心跳阀值的公式:


Renews threshold(n*2)*0.85=13

Renews (last min)n*2=16



实验1

1、开启集群版的eureka

2、注册6个服务到eureka


3、校对心跳阀值的公式


Renews threshold(4*2)*0.85=6

Renews (last min)4*2=8

4、关闭一个服务,等待90秒,观察该服务是否删除
实验2

1、开启集群版的eureka

2、注册两个服务到eureka


3、校对心跳阀值的公式


Renews threshold(4*2)*0.85=6

Renews (last min)4*2=8

4、关闭一个服务,等待自我保护的开启

7.2.为什么要启动自我保护

1、网络波动时保留坏数据(服务端没挂,但收不到心跳),consumer仍然能从eureka中获得provide的调用地址

2、网络恢复后会退出自我保护模式


3、微服务的负载均衡策略会剔除死亡的节点

7.3.如何关闭自我保护

1、修改 Eureka Server 配置文件

#关闭自我保护:会删除收不到心跳的服务eureka.server.enable-self-preservation=false

2、重新启动服务,然后关闭客户端进行测试:

此时我们发现,红色警告变成了自我保护被关闭的警告,且一分钟后实例被注册中心剔除,表明此时自我保护机制被关闭

     

8.注册信息完善与actuator

8.1.注册信息完善

修改前:

修改consumerapplication.properties

#该微服务在eureka上的显示名eureka.instance.instance-id=${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}#访问路径可以使用IP地址eureka.instance.prefer-ip-address= true

修改后:

            

8.2.actuator

修改前: 点击超链接服务报告ErrorPage

            

修改:

1、添加依赖

       <!-- actuator监控信息完善 -->

       <dependency>


           <groupId>org.springframework.boot</groupId>


           <artifactId>spring-boot-starter-actuator</artifactId>


       </dependency>

2、修改application.properties

#info页面展现配置info.app.name=springcloud-eureka-providerinfo.company.name=www.usian.cninfo.build.artifactId=springcloud_eureka_providerinfo.build.version=1.0-SNAPSHOT

完善info信息

     

监控服务的健康状况:

http://192.168.142.1:8771/actuator/health

                   

 

 

 


以上是关于初学eureka注册中心的主要内容,如果未能解决你的问题,请参考以下文章

springcloud-服务注册与发现

SpringCloud系列服务注册中心Eureka基础

微服务注册中心如何承载大型系统的千万级访问?

SpringCloud微服务小白入门之Eureka注册中心和服务中心搭建示例

SpringCloud系列四:Eureka 服务发现框架(定义 Eureka 服务端Eureka 服务信息Eureka 发现管理Eureka 安全配置Eureka-HA(高可用) 机制Eur(代码片段

注册中心 Eureka 源码解析 —— 调试环境搭建(含源码)