SpringCloud接入EDAS——服务发现篇

Posted Java paradise

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud接入EDAS——服务发现篇相关的知识,希望对你有一定的参考价值。

旁白

  

  很久没有写技术文章了,最近不是写水文就是写小说。说到底,还是最近很少研究技术的缘故,已经到了江郎才尽的地步了。

  不过,LZ无意间看到自己团队的小伙伴写的一些文章,觉得还是不错的,于是便动了心思,准备把这些文章拿来,也算填补一下最近技术文章缺乏的空白。

  而这些文章,不光涉及到一些技术干货,也算是变相的给自己团队的产品做了宣传,这也算是一石俩鸟了吧。

   

引言

  

  好了,接下来咱们进入正题。可以看到,本篇文章的标题里有三个关键字,SpringCloud、EDAS以及服务发现。关于SpringCloud和EDAS咱们接下来再说,LZ想首先简单介绍一下“服务发现”这个概念。

  说到服务发现,就不得不提到很火的一个概念“微服务”,究竟什么是“微服务”?

微服务是一个新兴的软件架构,就是把一个大型的单个应用程序和服务拆分为数十个的支持微服务。一个微服务的策略可以让工作变得更为简便,它可扩展单个组件而不是整个的应用程序堆栈,从而满足服务等级协议。

  以上是从百度上抄来的解释,从字面上粗暴的理解,微服务其实就是把一个大的应用拆分成很多的小应用,而这每一个所谓的小应用,就是所谓的微服务了。

  拆分成一个个微服务以后,可以得到不小的好处,最显而易见的就是,可以节省掉很多计算资源,因为你可以针对其中某一个模块进行扩缩容,而不再是只能对整个应用。

  举个例子,就拿淘宝来说,双11大促的时候,每个模块的压力都是不同的,比如订单系统、商品搜索、评价系统等等,其中显而易见的是,订单系统这部分的压力肯定很大,但是评价系统的压力就不会那么大。

  所以,大促期间,就可以把更多的计算资源往订单系统倾斜,而如果这些还都是集中在一个应用中的话,那就无法做到这样把资源充分利用了。

  不过,微服务虽然有诸多好处,但也会同样引入一些问题。

  最典型的问题,就是所谓的“服务发现”,那究竟什么叫服务发现?

  简单地说,一个应用改成微服务以后,大家都在这个池子里,谁发布了什么服务,这个事情是需要让池子里的各个微服务知道的,否则的话,服务之间调用就不知道该找谁了。

  所以,一个服务发布了以后,怎么“发现”它,就成了微服务体系中一个重要的事情。

  那么怎么解决这个事情,思路也很简单,拿淘宝来举例,我现在要买一个娃娃,怎么才能买到呢?

  首先第一件事是,卖娃娃的商家要把自己的店开到淘宝上,然后我去淘宝上搜索“娃娃”这个关键字,然后淘宝就告诉我一堆卖娃娃的商家,我再从这些商家里选出来一家,最终就可以买到我想要的娃娃了。

  那么结合以上买娃娃的过程,放到微服务这个“市场”里,也是类似的。首先得现有一个淘宝这样的平台,一般我们叫它“服务注册中心”,然后,每一个微服务模块发布了一个服务的时候,都需要到这个“服务注册中心”去注册一下,类似于卖家去淘宝上开店的操作。

  接下来,调用者一方在调用的时候,就先去“服务注册中心”查询一下,这个就相当于去淘宝上搜索“娃娃”的操作,最终,调用者从“服务注册中心”返回的服务提供者列表里选取一个,最终调用成功。

  当然了,在调用者选取服务提供者的时候,“服务注册中心”又或者是调用者自己,也可以有一定的排序算法,比如同机房的优先调用,或者压力小的优先调用等等。

  这其实在淘宝那个例子中也同样适用,比如你搜索“娃娃”,淘宝也会给你按销量或者是按评价,对卖娃娃的商家进行综合排序,以便给你筛选出最适合你的娃娃。

  

SpringCloud的服务发现

  

  好了,LZ先简单介绍了一下微服务和服务发现的概念,那么接下来,就来谈谈SpringCloud吧。

  SpringCloud是什么玩意呢?

Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems.

  上面这一段英文,是SpringCloud官网的解释,翻译过来的意思就是SpringCloud提供了一堆工具给开发者,让开发者可以很快的,构建一套通用模式的分布式系统。

  而这些SpringCloud提供的工具中,其中有一个,就是“服务发现”。

  在SpringCloud的支持体系中,有Eureka、Consul、Zookeeper等几种服务发现的支持,但是目前使用最广的,无疑就是Eureka了。接下来咱们就通过一个demo,来看看Eureka怎么使用的。

 

1. 启动一个服务注册中心(相当于上面例子中的淘宝网站)

 

  创建一个基础的 Spring Cloud 工程,命名为 eureka-server,并在 pom.xml 中引入需要的依赖内容:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
        <relativePath/>
    </parent>


    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
    </dependencies>

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

  通过 注解来启动一个服务注册中心。只需要在一个普通的 Spring Boot 应用中添加这个注解就能开启此功能,代码如下:

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

  这样启动时,应用将完全只用默认配置,如果想给服务命名,或者是修改监听端口,可以在 resource/application.properties 中进行如下配置。由于此工程就是唯一的一个 EurekaServer ,这里就不向自己注册自己了,将 register-with-eureka 设置成 false。

spring.application.name=eureka-server
server.port=8761
eureka.client.register-with-eureka=false 

  只需要直接运行 EurekaServerApplication 的 main 函数,eureka server 即可启动成功。启动成功以后,可以查看 http://localhost:8761 查看eureka server的详情。

  当然,页面打开成功,只是表明服务已经启动,目前 instances 为空,表明还没有服务注册上来。5a7b6781-f069-46b0-8df0-ab07ae4ca490.png

  
2.创建服务提供者(相当于上面例子中的卖家)

  

  创建一个 Spring Cloud 工程,命名为 service-provider。同样,首先在 pom.xml 中引入需要的依赖内容。

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
        <relativePath/>
    </parent>


    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>

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

  接着是服务提供端的代码,其中 @EnableDiscoveryClient 注解表明此应用需开启服务注册与发现功能。

@SpringBootApplication
@EnableDiscoveryClient
public class ServerApplication {

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

  既然是服务提供者,所以我们还需要提供一个简单的服务。

@RestController
public class EchoController {
    @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
    public String echo(@PathVariable String string) {
        return string;
    }
}

  最后同样是配置,除去配置应用名与监听端口外,还需要配置一下 Eureka Server 的地址。

spring.application.name=service-provider
server.port=18081
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

  启动 service-provider 服务,在 Eureka 页面查看服务是否已经注册成功,可以看到 instances 中已经存在的实例 service-provider,端口是 18081。

ab52a743-9e67-4be7-8c75-aa1bce6c9471.png

  

3.创建服务消费者(相当于上面例子中的买家)

  

  这个例子中,我们将不仅仅是演示服务发现的功能,同时还将演示 Eureka 服务发现 与 RestTemplate、AsyncRestTemplate、FeignClient这三个客户端是如何结合的。因为实际使用中,我们更多使用的是用这三个客户端进行服务调用。

  创建一个 Spring Cloud 工程,命名为 service-consumer。同样,首先在 pom.xml 中引入需要的依赖内容:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
        <relativePath/>
    </parent>


    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
    </dependencies>

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

  因为在这里我们要演示 FeignClient 的使用,所以与 service-provider 相比,pom.xml文件中的依赖增加了一个 spring-cloud-starter-feign。

  配置好依赖后,在启动函数里添加三个注解,使用 @EnableDiscoveryClient 注解启用服务注册与发现,使用 注解激活 FeignClients,添加 注解将 RestTemplate 与 AsyncRestTemplate 与服务发现结合。

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @LoadBalanced
    @Bean
    public AsyncRestTemplate asyncRestTemplate(){
        return new AsyncRestTemplate();
    }
    
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

}

  在使用 FeignClient 之前,我们还需要完善它的配置,配置服务名以及方法对应的HTTP请求,其中代码如下:

@FeignClient(name = "service-provider")
public interface EchoService {
    @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
    String echo(@PathVariable("str") String str);
}

  然后,我们就可以在 Controller 中直接使用他们。

@RestController
public class Controller {

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private AsyncRestTemplate asyncRestTemplate;
    @Autowired
    private  EchoService echoService;

    @RequestMapping(value = "/echo-rest/{str}", method = RequestMethod.GET)
    public String rest(@PathVariable String str) {
        return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
    }
    @RequestMapping(value = "/echo-async-rest/{str}", method = RequestMethod.GET)
    public String asyncRest(@PathVariable String str) throws Exception{
        ListenableFuture<ResponseEntity<String>> future = asyncRestTemplate.
                getForEntity("http://service-provider/echo/"+str, String.class);
        return future.get().getBody();
    }
    @RequestMapping(value = "/echo-feign/{str}", method = RequestMethod.GET)
    public String feign(@PathVariable String str) {
        return echoService.echo(str);
    }

}

  最后,还是不能忘了配置,特别是服务注册中心的地址。

spring.application.name=service-consumer
server.port=18082
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

  启动服务,分别进行调用,可以看到调用都成功了。不过需要注意,AsyncRestTemplate 接入服务发现的时间比较晚,需要在 Dalston 之后的版本才能使用,具体详情参见此 pull request

32b244d5-53c6-426c-9edd-13414c8fd91f.png

  

Eureka 的烦恼

  

  前面的例子在本机工作起来是很方便的,但是很遗憾,这只是一个 demo ,实际部署中我们可能都踩过坑或者有这么一些不爽。

  • 只有一个服务注册中心,显然这不符合高可用的原则,高可用就得增加
    eureka server 的数量,维护成本太高了。
  • 实际生产中,不会将服务注册中心与业务服务部署在同一台机器上。实际部署中,当 eureka server 的地址发生变化时,还得修改配置文件里 eureka server的地址,太麻烦了。
  • 实际使用中,服务注册发现中心的安全性也是需要考虑的,应该对服务注册和发现的请求进行鉴权,来确保服务的安全性,安全也是急需解决的问题。
  • eureka 使用过程中,有可能出现注册上去的服务地址不是一个 ip ,而是一个 hostname 的情况,事实上又无法通过 hostname 进行服务调用。其实只是因为没有增加 eureka.instance.prefer-ip-address=true这个配置,依旧需要添加配置。
  • eureka 因为缓存设计的原因,使得服务注册上去之后,最迟需要两分钟后才能发现。

  

  或许你希望有人提供一个安全、稳定、高可用、高性能、简单易用的服务注册中心。它的配置更加简单,而且又与原来写好的eureka代码完全兼容。

  这个怎么办呢?

  重点来了,LZ要插广告了,注意!

  EDAS就是你理想中的选择,你只需要简单修改几行代码,即可得到以下好处。

  • 稳定高可用的服务注册中心
  • 安全的服务注册、服务发现
  • 秒级的服务发现机制
  • 无需再关心服务注册中心的地址

  

EDAS 服务注册中心

   

1.安装轻量版配置中心(相当于Eureka的服务注册中心)

  

  这个安装过程很简单,这里就不再赘述了,详情可以参考 轻量级配置中心 。唯一需要注意的是,最后在应用启动时,需要配置一个 JVM 参数,配置如下。

//windows中修改startup.bat如下这一行
%_EXECJAVA% -Daddress.server.ip=%SERVER_IP% -Dvipserver.server.port=8080 -jar edas-config-center.jar

//linux中修改startup.sh如下这一行
nohup $JAVA -Daddress.server.ip=$SERVER_IP -Dvipserver.server.port=8080 -jar edas-config-center.jar >/dev/null 2>&1 &

  然后按照文档所描述,windows下直接执行startup.bat,linux下执行startup.sh即可。

  最终,你也会看到一个和Eureka类似的页面,当然,现在这个页面当中是没有服务的。

  另外,EDAS的轻量版配置中心启动以后,不光内嵌了一个简单的开发版服务注册中心,还包含了一个简单的分布式配置服务,以下这个页面可以进行配置的维护操作。

  这个分布式配置服务相当于SpringCloud体系中的Spring Cloud Config,不过这个和本文主线没有什么关系,这里就暂且不详谈了。

  

2.服务提供者和消费者代码修改

  

  关于Java源码的修改,只有两行,需要在 main 函数中添加两行,修改之后的 service-provider 的 main 函数如下。

public static void main(String[] args) {
    PandoraBootstrap.run(args);//引入Pandora
    SpringApplication.run(ServerApplication.class, args);
    PandoraBootstrap.markStartupAndWait();//引入Pandora
}

  pom.xml 的修改有两点,一个是将原来的 eureka 的 starter 替换成 EDAS 服务注册中心的starter,并加入 pandora 的依赖。修改之后的service-provider 的pom文件如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-vipclient</artifactId>
        <version>1.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-pandora</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

  另外一个则是在 build 的 plugins 中,也需要修改成 EDAS 的方式,修改后的内容如下,版本号后续可能会升级。

<build>
    <plugins>
        <plugin>
            <groupId>com.taobao.pandora</groupId>
            <artifactId>pandora-boot-maven-plugin</artifactId>
            <version>2.1.7.8</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

  最后一步,由于目前以上jar包尚未进入中央仓库,所以还需要配置下 maven 的私服地址,修改settings.xml配置如下。

<settings>
    <localRepository>/Users/../.m2/repository</localRepository> 
    <profiles>
        <profile>
            <id>nexus</id>
            <repositories>
                <repository>
                <id>central</id>
                <url>http://repo1.maven.org/maven2</url>
                <releases>
                    <enabled>true</enabled>
                </releases>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
                </repository>
            </repositories>
            <pluginRepositories>
                <pluginRepository>
                <id>central</id>
                <url>http://repo1.maven.org/maven2</url>
                <releases>
                    <enabled>true</enabled>
                </releases>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
                </pluginRepository>
            </pluginRepositories>
        </profile>
        <profile>
            <id>edas.oss.repo</id>
            <repositories>
                <repository>
                    <id>edas-oss-central</id>
                    <name>taobao mirror central</name>
                    <url>
                    http://edas-public.oss-cn-hangzhou.aliyuncs.com/repository
                    </url>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                </repository>
            </repositories>
            <pluginRepositories>
                <pluginRepository>
                    <id>edas-oss-plugin-central</id>
                    <url>
                    http://edas-public.oss-cn-hangzhou.aliyuncs.com/repository
                    </url>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                </pluginRepository>
            </pluginRepositories>
        </profile>
    </profiles>
    <activeProfiles>
        <activeProfile>nexus</activeProfile>
        <activeProfile>edas.oss.repo</activeProfile>
    </activeProfiles>
</settings>        

  OK,到这里所有的都已经修改好了,service-consumer 的修改方式与 service-provider 的修改方式完全一样,这里就不再赘述了。

  当你将service-provider和service-consumer启动以后,便可以在轻量版配置中心的页面当中看到对应的服务提供者和调用者。

  当然,你也可以直接从官网上直接下载写好的demo。

server-demo

client-demo

 

EDAS轻量版配置中心原理简介

  

  换了一个pom依赖,加了两行代码就把 Eureka 替换成了 EDAS 服务注册中心,虽然方便,但是这对于你来说也许相当于是一个黑盒,黑盒总是让人很没有安全感。下面LZ将从服务注册中心寻址、服务注册和下线、客户端结合、高可用和安全多个方面,来简单介绍一下。

  

服务注册中心寻址

 

  既然不需要在配置文件里配置服务注册中心的地址了,那么客户端是如何找到服务中心的呢?
  其实是通过一个http请求来实现的,地址为http://jmenv.tbsite.net/vipserver/serverlist。无论是服务端还是客户端,都会利用这个地址来进行服务发现,这个地址就相当于在Eureka中的http://localhost:8761/eureka这个地址的作用。