距离上一次写博客已经差不多过去有一年了,这期间我的技术栈发生了比较大的改动,从开发安卓改为了Java服务端开发,毕竟在大学里,时间比较充裕,期间学完了JavaEE的一些基本组件和SSM三大框架,netty框架等,还浅显地尝试了一下redis,activeMQ等中间件。甚至在去年下半年还花了差不多几个月的时间去学习了Hadoop生态体系的一些组件,但是感觉我学习总是蜻蜓点水,学过很多但是都很浅。不过好在最近从游戏的泥潭***之后终于确立了自己的目标,大学的剩余一半时间有了专攻的方向,希望在明年的现在我已经实现了自己的目标。共勉!

    之前学习一直没有记录成博客,自己是真的懒,不过感觉学过的很多东西过一段时间就会遗忘,可能写博客的话会加深一些印象,所以这次开新坑我决定全程用博客来记录自己的学习历程。(本博客性质类似与学习笔记,可能有些地方会有错误或者写的不清楚,希望指出)

    正式开始,Spring Cloud是一整套的微服务架构的解决方案,它涵盖了相当多的组件,比如服务治理组件Eureka,客户端负载均衡组件Ribbon,服务容错保护组件Hystrix,声明式服务调用组件Feign,API网关治理组件Zuul,分布式配置中心组件Config,消息总线组件Bus,消息驱动组件Stream和分布式服务跟踪组件Sleuth,可以说是覆盖到了微服务架构的方方面面,昨天和今天,我正式开始学习了Spring Cloud的第一个组件——服务治理组件Eureka

    Eureka是Netflix Eureka做了二次封装之后的一个Spring Cloud组件,因为Spring Cloud是基于spring boot的,所以我们只需要引入简单的依赖和注解配置就可以轻松地整合spring boot微服务应用和Eureka服务治理中心。

    为什么需要服务治理中心我在这里就不细说了,这里主要说一下如何使用以及它的简单原理

    服务治理主要需要完成两件事,一是服务的注册,二是服务的发现,服务注册简单来说就是服务提供方把自己所提供的服务注册到服务注册中心,就可以供服务调用方调用,而服务发现就是服务调用者可以不指定具体实例地址的情况下通过服务名向服务注册中心发起请求,从而完成服务的调用。

    Eureka分为服务端和客户端,服务端顾名思义就是服务注册中心,而客户端主要完成的就是服务的注册和发现,注意它在这里实现了两个功能,就是说Eureka的客户端即是服务提供者也是服务的请求方,接下来我们就正式开始搭建Eureka的服务端和客户端。

    一:搭建服务注册中心:

    首先创建一个基础的spring boot项目,引入一些必要的依赖,如下面的pom文件:(这里我一开始总是出错,这个pom文件也是参考了网上其他人成功的pom文件改写的)

    

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>1.5.7.RELEASE</version>
   <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
   <java.version>1.8</java.version>
   <spring-cloud.version>Dalston.SR4</spring-cloud.version>
</properties>

<dependencies>
   <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka-server</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>

之后的事情很简单,只需要通过@EnableEurekaServer注解就可以启动一个服务注册中心了,如下所示:

@EnableEurekaServer
@SpringBootApplication
public class SpringcloudEurekaTestdemoApplication {

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

在默认的配置下,服务注册中心也会尝试注册自己,为什么会这样呢,稍后说到高可用的时候会谈,在这里我们想创建一个单节点的服务注册中心,所以修改一些配置,如下

server.port=1111
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

其中三四行就是不向注册中心注册自己的代码,完成上面的配置之后,启动应用,访问http://localhost:1111/就可以看到如下图所示的面板


这里Instances一栏是空的,说明还没有服务在这里注册过,那么接下来我们就注册一个服务提供者在上面:

二:注册服务提供者:

我们还是新建一个spring boot项目,并且写一个简单的请求处理接口,首先引入必要的依赖如下:

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>1.5.7.RELEASE</version>
   <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
   <java.version>1.8</java.version>
   <spring-cloud.version>Dalston.SR4</spring-cloud.version>
</properties>

<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</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>

我们可以看到配置大同小异,接下来写一个简单的接口

@RestController
public class HelloController {

    private final Logger logger = Logger.getLogger(getClass());

    @Autowired
    private DiscoveryClient client;

    @RequestMapping(value = "/hello",method = RequestMethod.GET)
    public String index(){
        ServiceInstance instance = client.getLocalServiceInstance();
        logger.info("/hello,host:" + instance.getHost()+",service_id:"+instance.getServiceId());
        return "Hello Nkpdqz!";
    }
}

这里我们注入了DiscoveryClient对象,用来打印出服务的相关内容,最后在主类中加上@EnableDiscoveryClient注解,就可以激活DiscoveryClient实现

@EnableDiscoveryClient
@SpringBootApplication
public class SpringcloudQuickstarterBootdemoApplication {

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

最后在配置文件中写明服务名和服务注册中心的地址:

spring.application.name=hello-service
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/

之后运行一下,大功告成!现在我们就有了一个已经注册好的服务。

三:高可用注册中心:

高可用的原理很简单,就是设置多个服务中心,让他们互相注册,实现服务清单的同步,就可以达到高可用的效果了,这里本来也有一个例子,在这里我就不详细阐述了,有兴趣的可以看相关书籍完成高可用注册中心的搭建。

四:服务发现与消费:

这里我们模拟一个服务的调用方,还是先创建一个springboot项目,引入相关的依赖,这里我们引入了下一次要学的Ribbon组件,当作客户端的负载均衡器:

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>1.5.7.RELEASE</version>
   <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
   <java.version>1.8</java.version>
   <spring-cloud.version>Dalston.SR4</spring-cloud.version>
</properties>

<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</artifactId>
   </dependency>

   <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-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>

创建主类,加上@EnableDiscoveryClient和@LoadBalanced注解,第二个注解的作用是开启客户端负载均衡:

@EnableDiscoveryClient
@SpringBootApplication
public class SpringcloudEurekaConsumerDemoApplication {

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

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

接下来创建一个模拟请求访问并提供给请求调用者的类:

@RestController
public class ConsumerController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET)
    public String helloConsumer(){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody();
    }
}

它实现了/ribbon-consumer接口,它内部实现了通过访问HELLO-SERVICE服务名而请求到真正的/hello接口,这是一个很重要的特性,也是服务治理的一种体现,最后配置一下

spring.application.name=ribbon-consumer
server.port=9000

eureka.client.serviceUrl.defultZone=http://localhost:1111/eureka/

这样一个简单的服务消费者也就创建完成了

最后运行一下,就可以在服务注册中心看到注册好的HELLO-SERVICE和RIBBON-CONSUMER两个服务了,当对http://localhost:9000/ribbon-consumer发起get请求,就会成功返回Hello Nkpdqz。同时还输出了一些其他有用的信息,比如各个实例的请求总数等。

实例部分就在这里说完了,本来还有一些原理分析和源码分析,这些留到以后再专门写一系列博客记录。