欢迎来到“世界和平”的主页

Spring-cloud微服务 Eureka学习教程-分布式搭建EurekaServer、EurekaClient(中级)

        我们这里只有一台服务器,所以我们先仿集群搭建。

         完整demo项目代码:https://github.com/wades2/EurekaDemo2

        在这之前我们先分析分析Eureka相比其他注册中心的好处。在一般的应用过程中,如果注册中心service出现了问题,然而没有备用的节点去替代这个主节点去分发服务,就会造成相关注册服务的瘫痪,因此我们在分布式架构中,都会有备用节点。

        我们先看看Doubel,在doubel中,zookeeper(以下简称zk)是作为服务注册中心的,在集群配置中,会有一个主从关系节点。如果Master挂了, zk就会启动节点推举,而在节点推举的过程中,整个服务是不能使用的。但是zk强调的是CAP理论中的CP强调高的一致性,【注:什么是CAP理论:Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得】,在实际上线的站点或者网站中,30~60s的选举过程导致站点不可用,损失是很大。

         

      

而我们再看看Eureka(如上图),Eureka是去中心化的,每个Eureka server都是平级的。Eureka取CAP中的AP,注重可用性,Eureka内置了心跳服务(用于淘汰一些“濒死”的服务器),即使当你的应用服务某个server挂掉了,其他的服务是可以立即顶上去,保证客户端向其发起请求有响应。

       两者的对比我们先大致分析到这里。具体的分析可以看我转的一篇博客:

       https://blog.csdn.net/asd529735325/article/details/85049662

      接下来我们尝试自己搭建一个分布式的包含:Eureka Server,Application Service,Application Client的Eureka注册中心。

      通过上面这张图我们可以看到,一个Eureka有三个角色:

     1、Eureka Server(通过Register, Get,Renew等接口提供注册和发现)
     2、Application Service(服务提供方,把自身服务实例注册到Eureka Server):
     3、Application Client(Service Consumer):服务调用方,通过Eureka Server获取服务实例,并调用Application Service

 

     

他们主要进行的活动如下:

每个Region有一个Eureka Cluster, Region中的每个Zone都至少有一个Eureka Server。
Service作为一个Eureka Client,通过register注册到Eureka Server,并且通过发送心跳的方式更新租约(renew leases)。如果Eureka Client到期没有更新租约,那么过一段时间后,Eureka Server就会移除该Service实例。
当一个Eureka Server的数据改变以后,会把自己的数据同步到其他Eureka Server。
Application Client也作为一个Eureka Client通过Get接口从Eureka Server中获取Service实例信息,然后直接调用Service实例。
Application Client调用Service实例时,可以跨可用区调用。(【引】)

     

        在上面一篇文章中我们已经搭建好了Eureka Server和Application Client,如果不清楚的朋友建议可以先看看这篇博文(https://blog.csdn.net/asd529735325/article/details/84992538,git代码:https://github.com/wades2/EurekaDemo ),再来本篇中一起探讨学习。

        

        为了保护eureka安全性,在上一篇的基础上,我加了service访问密码。因此在client端和集群配置的service都需要加上相互的访问密码。

        先看看我个人demo大致架构吧:

其中:EurekaServer模块和Eurekaserver_back是两个相互平级依赖的server,EurekaClient是我们服务提供方,EurekaCaller是我们服务的调用方。大致的调用过程如下图:

 

基于上一篇的基础,我们从新看看pom文件和相关配置。

首先是Eureka Server(此处我们是集群配置,因此有两个server)

首先修改电脑的hostname,添加:127.0.0.1 localhost server1 server2,作为两个server的相互依赖。

两个server的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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.M3</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.3.6.RELEASE</version>
        </dependency>

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


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


</project>

相关注意点在上一篇博客有提到,这里就不赘述。

EurekaServer的配置文件:

# 应用程序的名称
spring.application.name=Eureka-server2

security.user.name=user
security.user.password=123456
#修改启动端口
server.port=8083
# 是否将该实例的注册信息注册到Eureka服务器上,在只有一个Eureka服务器的情况下没必要,只是用于实例的发现
eureka.client.register-with-eureka=true

# 是否向Eureka服务器获取注册信息,在单实例的Eureka中共没必要
eureka.client.fetch-registry=true
#Eureka Server能够迅速有效地踢出已关停的节点,但是新手由于Eureka自我保护模式,以及心跳周期长的原因,常常会遇到Eureka Server不踢出已关停的节点的问题
# 设为false,关闭自我保护
eureka.server.enable-self-preservation=true
#清理间隔
eureka.server.eviction-interval-timer-in-ms=6000

eureka.client.serviceUrl.defaultZone=http://user:123456@server2:8082/eureka

eureka.instance.hostname=server1

EurekaServer_back的配置文件:

# 应用程序的名称
spring.application.name=Eureka-server2

security.user.name=user
security.user.password=123456
#修改启动端口
server.port=8082
# 是否将该实例的注册信息注册到Eureka服务器上,在只有一个Eureka服务器的情况下没必要,只是用于实例的发现
eureka.client.register-with-eureka=true

# 是否向Eureka服务器获取注册信息,在单实例的Eureka中共没必要
eureka.client.fetch-registry=true
#Eureka Server能够迅速有效地踢出已关停的节点,但是新手由于Eureka自我保护模式,以及心跳周期长的原因,常常会遇到Eureka Server不踢出已关停的节点的问题
# 设为false,关闭自我保护
eureka.server.enable-self-preservation=true
#清理间隔
eureka.server.eviction-interval-timer-in-ms=60000

eureka.client.serviceUrl.defaultZone=http://user:123456@server1:8083/eureka

eureka.instance.hostname=server2

因为我们引入了security的jar包,保证访问server页面的安全性,因此,在client和其他节点配置的时候,都应该加上用户名和密码,把defaultZone改为:http://用户名:密码@hostname:端口号/eureka的形式。

【注意!!!!,这里因为是相互依赖,所以在EurekaServer中defaultZone使用的是EurekaServer_back的用户名,密码和它对应的hostname+端口,同理EurekaSerer_back对应的是EurekaServer的。】

 

运行类都一样:

package com.example.demo;

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

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

然后我们可以启动一个server访问一下:

访问需要用户名密码,就是配置文件里面的security.user.name,和security.user.password,我们可以看到界面如下:

这里我们可以看到因为我们8082端口的server因为没有启动,所以无法依赖到,但是已经注册进去了。我们再启动server_back可以看到界面如下:

 

 

这样的界面就是注册成功了。然后我们开始第二部:

Application Service注册到server里面,也就是我们上一篇说的client端,也就是我这个demo的EurekaClient:

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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.M3</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

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

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


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.3.6.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


</project>

 

application配置文件:

spring.application.name=Eureka-client

#eureka.client.allow-redirects=false
#修改启动端口
server.port=8084
eureka.client.serviceUrl.defaultZone=http://user:123456@localhost:8083/eureka,http://user:123456@localhost:8082/eureka
#eureka.client.register-with-eureka=true
eureka.instance.ip-address=true

启动类:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@EnableEurekaClient
//@EnableDiscoveryClient
@ComponentScan(basePackages = {"com.example.demo.controller"})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

这里我们设置了扫描controller包下面的java文件,我的demo中contorller如下:

其中UserController放置在controller这个包下,还有一个作为测试的Contrller放置在启动类平级目录(你也可以随便放一个,我们只是做后期实验的)

我们的UserController

package com.example.demo.controller;

import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.DiscoveryClient;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.converters.Auto;
import com.netflix.discovery.shared.Applications;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;


@RestController
@RequestMapping("user")
public class UserController {
//    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

//    @Autowired
//    private IUserService userService;

//    @GetMapping("getUser")
//    public List<UserEntity> getUser() {
//        UserEntity user = new UserEntity();
//        return userService.getUser();
//    }
    @Autowired
    private EurekaClient eurekaClients;

//    @Autowired
//    private DiscoveryClient discoveryClient;

    @GetMapping("getUser")
    public String getUser() {
        //todo 得到eureka server的服务实例
        InstanceInfo info=eurekaClients.getNextServerFromEureka("Eureka-client",false);
//        return info.getHomePageUrl();
        return "hello one";
    }


}

我们的Controller:

package com.example.demo;

import com.netflix.appinfo.InstanceInfo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("user")
public class Controller {
    @GetMapping("getUser2")
    public String getUser() {
        return "hello tow....";
    }
}

然后启动。

我们可以看到如下界面:

这里client已经注册进来了,然后我们可以访问一下http://localhost:8084/user/getUser

访问没问题。

访问http://localhost:8084/user/getUser2,报错,因为我们启动类没有扫描其他的包。

 

最后一步:Application Client(Service Consumer):服务调用方,通过Eureka Server获取服务实例,并调用Application Service

也就是我们的EurekaCaller:

直接使用Eureka Client还是比较麻烦的,幸运的是,RestTemplate整合了EurekaClient,Ribbon为我们提供了多样的负载均衡的功能,为我们提供了很多便利,我们所需要做的就是在Spring中注册一个RestTemplate,并且添加@LoadBalanced 注解。

加入Ribbon的原因是服务调用者不关心服务提供者有多少个服务实例,它只关注它要调用这个服务ID,因而可能需要一定的负载均衡。

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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.RC1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>

</project>

application配置文件:

spring.application.name=Eureka_caller
server.port=8086
eureka.client.serviceUrl.defaultZone=http://user:123456@localhost:8083/eureka,http://user:123456@localhost:8082/eureka

启动类一样扫描controller:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan({"com.example.demo.controller"})
@EnableEurekaClient
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

我们这里的controller开始通过Rest客户端映射调用注册的Eureka_client。

package com.example.demo.controller;

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

@RestController
@Configuration
public class Controller {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    @GetMapping("getUser/routine")
    public String routine() {
        RestTemplate restTemplate = getRestTemplate();
        String json = restTemplate.getForObject("http://Eureka-client/user/getUser", String.class);
        return json;
    }

    @GetMapping("getUser/routine2")
    public String routine2() {
        RestTemplate restTemplate = getRestTemplate();
        String json = restTemplate.getForObject("http://Eureka-client/user/getUser2", String.class);
        return json;
    }
}

restTemplate.getForObject方法请求特定的服务url,注意里面使用的Eureka-client正是我们刚才在服务提供者客户端项目中配置的服务名称,它已经被注册到Eureka服务上。

然后我们启动,访问http://localhost:8086/getUser/routine

我们可以看到内容和访问http://localhost:8084/user/getUser一样,但是依旧访问http://localhost:8086/getUser/routine2访问不到,其实要想访问到很简单,在EurekaClient扫描中带上Conrtoller所在的包就可以了!

OK,全文结束,接下来我们讲解,如何充分利用ribbon来动态调用服务消费者。

posted @ 2018-12-17 16:13  yiyan00  阅读(240)  评论(0编辑  收藏  举报