文章中涉及到的源码已上传github:https://github.com/BLF2/SpringCloud
本次学习spring cloud采用的注册中心是Eureka,服务提供者是常见的spring boot应用。
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
Eureka包含两个组件:Eureka Server和Eureka Client。
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
(以上来自于百度百科)
架构相对简单,服务提供者向服务注册中心注册自己的服务,服务消费者从服务注册中心获取服务(被称为服务发现),然后消费服务,关于服务消费者的相关内容后续再陆续介绍。当前架构足够简单,可能有一些疑问:为什么服务消费者不直接调用服务提供者,而是通过服务注册中心调用呢?考虑一个问题:如果服务提供者只有一个,某一天宕机了?下游服务是不是全部挂掉了?如果这个服务是支付服务呢?公司的日常现金流入是不是就断掉了?这对公司来说是及其不利的。再比如服务提供者这个单体应用使用的资源已经达到当前实体服务器上限,我们是扩服务器资源还是多加一台服务器,再部署一个同样的应用呢?这个问题不着急回答,我们考虑现实问题:盖一座大楼一个人需要360天,现在我想180天就盖完,是加一个人还是找一个更加壮实的人?如果我想30天盖完呢?可见人力有时穷,我们通常的解决方案是多个人协同合作,而不是找更壮实的人(也不一定能找到),映射到架构上就是:给服务器加配置是最简单,最快速的办法,但配置是有极限的,因此,为了高可用,我们使用多机集群部署,于是就有了注册中心,服务提供者向注册中心注册,服务消费者向注册中心询问服务。高可用架构下,服务注册中心和服务提供者都是多机集群部署,如果某一个注册中心挂了,或者某个服务挂了,并不影响整体的运行,达到高可用的目的。
打开IDEA,选择File->New->Project,出来的对话框选Empty Project,然后Next,出来的对话框中Project Name填SpringCloud,路径选择自己的,然后点Finish,初始化完成后会弹出让你创建Module的对话框,点上面的+号,选New Module,然后选Spring Initializr
,然后点Next,填写Group(例如net.blf2),Artifact(我这里填写的是serviceprovider),然后点Next,在Web下选择Spring Web Starter,然后点Next,填写项目名为:service-provider-8001(8001为端口名,为了后面部署多个项目做准备),然后Finish,等待生成项目结构。
在主包(我的是net.blf2.serviceprovider)下创建controller包,并在controller包下创建HomeController类,写一个接口用于测试,代码如下:
package net.blf2.serviceprovider.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/hello")
public class HomeController {
@GetMapping("/hi")
public Map<String,String> hello(){
return new HashMap<String,String>(){{
put("code","000000");
put("msg","Hello World-8001");
}};
}
}
application.properties重命名为application.yml(这一步可做可不做,我个人比较喜欢yml风格的配置),配置端口为8001:
server:
port: 8001
然后启动项目,用Postman调用一下:
返回成功,至此,服务提供者构建完毕,其实就是一个基本的Spring Boot的web项目。
按照上面介绍的步骤创建Module,Group为net.blf2,Artifact为eurekaserver,Project Name为eureka-server-9001,在dependencies对话框选择Eureka Server如下:
创建成功后配置application.yml(application.properties):
server:
port: 9001
eureka:
instance:
hostname: localhost #配置Eureka Server的名字(地址)
client:
register-with-eureka: false #自己不作为服务注册自己
fetch-registry: false #不拉取服务注册信息
service-url:
defaultZone: http://localhost:9001/eureka/ #注册中心服务注册地址,多个用,隔开
spring:
application:
name: eureka-server-${server.port} #这个名字尽量配置,方便我们记忆和查看
启动服务,访问localhost:9001,如下表示启动成功:
添加Eureka-Client依赖并且向Eureka-Server进行注册,具体做法是:
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
<relativePath/>
parent>
<groupId>net.blf2groupId>
<artifactId>service-provider-8001artifactId>
<version>0.0.1-SNAPSHOTversion>
<name>service-provider-8001name>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
package net.blf2.serviceprovider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class, args);
}
}
server:
port: 8001
spring:
application:
name: service-provider-8001
eureka:
client:
service-url:
defaultZone: http://localhost:9001/eureka/ #注册中心地址
正常情况下来讲,我们应该这么做:把service-provider-8001打为jar包,然后用java -jar 包名.jar --server.port=8001
来启动service-provider-8001,然后java -jar 包名.jar --server.port=8002
来启动一个新的一样的服务,但我们为了直观性,我们这么做:
创建新的Module,Project Name 为service-provider-8002,完整application.yml为:
server:
port: 8002
spring:
application:
name: service-provider-8002
eureka:
client:
service-url:
defaultZone: http://localhost:9001/eureka/
其它内容均与service-provider-8001相同,然后启动这个服务提供者,服务注册中心如下:
可见service-provider-8001和service-provider-8002都在服务注册中心看得到,说明我们的服务已经注册成功。
正常情况下来讲,我们应该使用命令使用9001和9002端口启动两个服务注册中心,但为了直观,我们这么做:
server:
port: 9001
eureka:
instance:
hostname: peer1 #配置Eureka Server的名字(地址)
client:
register-with-eureka: false #自己不作为服务注册自己
fetch-registry: false #不拉取服务注册信息
service-url:
defaultZone: http://peer2:9002/eureka/ #把当前注册中心告诉peer2
spring:
application:
name: eureka-server-${server.port} #这个名字尽量配置,方便我们记忆和查看
server:
port: 9002
eureka:
instance:
hostname: peer2
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://peer1:9001/eureka/
spring:
application:
name: eureka-server-${server.port}
127.0.0.1 peer1
127.0.0.1 peer2
然后启动两个服务注册中心,启动两个服务提供者,访问9001和9002端口,可以看到如下:
---------------------------我是分割线---------------------------
第一张图是访问9001端口的,可以看到注册中心里面显示有peer2,服务提供者有server-provider-8001和server-provider-8002,
第一张图是访问9002端口的,可以看到注册中心里面显示有peer1,服务提供者也有server-provider-8001和server-provider-8002,回看我们的服务提供者配置中的eureka.client.service-url.defaultZone: http://localhost:9001/eureka/
配置项,可以发现无论是service-provider-8001还是service-provider-8002的这项参数都指向了9001端口的注册中心,但是上面第二幅图也显示出了两个服务提供者。这就说明了两个eureka是同步的,进而类比到集群就是:只要我向服务注册中心集群任意一个节点注册我的服务,整个集群都会知道,从而达到高可用的目的。这也证明了我们的服务注册中心搭建高可用集群已经成功。