SpringCloud入门搭建及服务调用

Posted

tags:

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

参考技术A 开发工具:idea 2020.2.3
java:1.8
maven:3.3.9
SpringBoot:2.1.3.RELEASE
SpringCloud:Greenwich.SR5 (版本和SpringBoot必须对应,对应表自行百度)

idea配置我就不细说了

然后next

finish

然后配置pom.xml:
一般在父项目配置

然后配置依赖,这里只是一个springboot项目,所以配一个springboot就行了:

然后配置pom.xml,它们俩都是独立的springboot项目了,这里也可以和父类工程做依赖继承,但是这里就没必要,更凸显服务的独立性:

controller:

power启动类:

UserController.java
在这里调用另一个服务power服务的接口,可以使用reatTeplate来实现,需要配置

reaTeplate配置文件:AppConfig.java

user服务启动类:

user服务的 application.yml

同样可以以相同方式在power服务中调用user的接口
然后在浏览器:
127.0.0.1:6060/power/getPower.do(6060是power服务的端口)
127.0.0.1:7070/user/getUser.do调用自己的接口(7070是user服务的端口)
127.0.0.1:7070/user/getPower.do调用power的接口

至此实现了不同服务之间的调用

Springcloud使用入门

Springcloud是一款微服务框架,它基于Springboot,可以使用它用来管理纵向拆分的项目,将一个个的小项目变成微服务。下面简单的搭建一个微服务,使用eureka组件实现注册中心,使用ribbon组件实现微服务调用,使用zuul网关实现真实服务地址和访问地址的分离,其他技术后续补充。

springcloud和nginx

前面学习过nginx,它除了可以实现动静分离,还可以实现负载均衡,即可以将项目做成分布式tomcat集群,前端访问可以随机或者按照权重值访问后端服务。但是nginx有一个小缺点,如果单台服务器出现问题、或者需要添加服务器,需要修改conf.xml配置文件,或某台服务器出现问题如何容错等,ngnix是无法实现。简单来说它缺少管理者的因子,需要人为\'\'介入\'\',在这种情况下,springcloud就可以弥补nginx的不足,它不仅仅可以实现分布式,负载均衡,并且对微服务有管理、容错、监听的能力。

springcloud

springcloud是spring家族的一员,它是一个微服务框架,用在大型分布式应用的开发,官网:https://spring.io/projects/spring-cloud,除了springcloud外,其他比较出名的微服务框架还有dubbo。

如果一个项目比较小,直接使用springboot,或者ssm做成单体项目就可以,但是如果一个项目比较大,就需要考虑到拆分,比较常见的就是横向拆分和纵向拆分。

(1)纵向拆分:大项目分成很多小项目,如电商网站的登录注册系统、商品系统、秒杀系统、搜索系统、购物车系统等,都是拆分后的小项目,这就是纵向拆分。

(2)横向拆分:一个纵向拆分后的项目,也可以继续拆分,如有人专门做控制层,有人做业务层,这就是横向拆分。

springcloud是如何对拆分后的项目进行管理的呢,这就需要用到它的关键组件,这里暂时先记录eureka、ribbon和zuul三种组件,其他组件如config、feign和hystrix后续单独补充。

(1)eureka:服务治理组件,利用它的注册和发现机制,实现整体微服务的管理,提供其他服务注册,并可以发现其他服务,springcloud只提供了一种这种治理组件。

(2)ribbon:通过eureka发现机制,可以负载均衡、随机或者根据权重来调用其他提供服务的组件。

(3)zuul:网关组件,唯一对外暴露接口地址的组件,实现外界访问地址和内部请求地址的分离,对外影藏了真实的地址。

下面使用一下这几个组件。

eureka

整个springcloud的核心组件之一,它就像一个\'\'管理者\'\',利用自己的注册和发现的能力,来管理着一批注册在它身上的微服务。即其他服务可以注册在eureka注册中心,它管理着一批这样的已经注册的微服务,并对外提供服务,同时注册中心会将其他服务注册的信息保存到本地,通过定时更新来维护和其他服务的关系。简单来说注册中心就像\'\'工商局\'\',各种公司之类的服务提供者都在这里注册,完了就可以对外提供服务了。

a.搭建eureka注册中心

使用IDEA,搭建一个eureka注册中心,搭建是基于springboot,需要利用springboot的自动配置和\'约定大于配置\'的特点,来配合springcloud简化搭建过程,注意两者版本的兼容,可以参考文末博客。

(1)pom文件导入springboot父pom、以及springcloud依赖。

<?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.boe</groupId>
  <artifactId>Eureka-server01</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>Eureka-server01</name>
  <!-- FIXME change it to the project\'s website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <!--使用springboot的资源,结构特性。另外使用springcloud的所有依赖环境-->

  <!-- 基于springboot,继承springboot-parent,引入springboot的父pom文件 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <!--1.5.9是稳定版本-->
    <version>1.5.9.RELEASE</version>
  </parent>

  <!--导入springcloud的父pom资源,声明式依赖,子工程手动导入时就导包,不导入就不导入包-->
  <dependencyManagement>
    <dependencies>
      <!--导入springcloud-->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Edgware.RELEASE</version>
        <!--注意通过坐标找到的spring-cloud-dependencies-Edgware.RELEASE.pom文件中,打包方式为pom,因此需要这里type为pom-->
        <type>pom</type>
        <!--maven scope方式常用有很多种,默认为compile,还有test、provided、system等-->
        <scope>import</scope>
      </dependency>

    </dependencies>
  </dependencyManagement>

  <!--以上两步,完成继承父springboot pom依赖,并导入springcloud的依赖管理-->

  <dependencies>

    <!--引入eureka注册中心的依赖-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka-server</artifactId>
      <!--建议使用下面的artifactId-->
      <!--<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>-->
    </dependency>

    <!-- spring-boot-starter-web 已经在eureka-server中传递,因此这里不需要配置springboot的starter-web-->

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!--springboot运行jar包的插件,可以直接使用java -jar jar包名来运行springboot-->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>

      <!--指定jar包运行的main class-->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <!--<configuration>-->
          <!--<archive>-->
            <!--<manifest>-->
              <!--<addClasspath>true</addClasspath>-->
              <!--<mainClass>com.StarterEurekaServer</mainClass> &lt;!&ndash; 此处为主入口&ndash;&gt;-->
            <!--</manifest>-->
          <!--</archive>-->
        <!--</configuration>-->
      </plugin>

    </plugins>
  </build>

</project>

(2)资源目录下添加application.properties文件,添加服务端口、服务名、注册eureka中心地址等信息。

# 端口
server.port=8088
# 客户端通过ip底层注册
# eureka.client.prefer-ip-address=true
eureka.client.preferIpAddress=true
#暂时先关闭,当前注册中心作为客户端
# 在注册中心本身,使得具有注册和发现的能力,如果要实现HA,需要改成true,这样就会访问注册中心的接口
eureka.client.regist-with-eureka=false
# 关闭发现抓取注册信息的能力
eureka.client.fetch-registry=false
# 注册中心的地址,http请求的接口
eureka.client.serviceUrl.defaultZone=http://localhost:8088/eureka

# 当前工程提供一个服务名称,相同功能的工程创建集群,都是一个名称,以当前服务名为key保存在当前服务的所有节点信息的map对象中
# 如果是同一个name,就可以创建HA高可用
spring.application.name=eureka-server

# 可以关闭自我保护,没有收到续约,就从注册中心剔除服务
# eureka.server.enable-self-preservation=false

(3)写一个启动类,来启动注册中心,需要使用@EnableEurekaServer注解,来表名启动的是一个注册中心服务类。

package com;

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

/**
 * eureka server启动类
 */
@SpringBootApplication
@EnableEurekaServer
public class StarterEurekaServer {
    public static void main(String[] args) {
        SpringApplication.run(StarterEurekaServer.class,args);
        System.out.println("I am eureka server01");
    }
}

(4)启动后,访问注册中心地址localhost:8088,就可以查看注册中心注册的实例,这里有一个就是eureka服务自己本身,以后如果有注册的服务,都能在这里查看。

b.搭建一个微服务并注册

Eureka注册中心搭建起来后,接下里搭建一个微服务并注册在Eureka注册中心,并使用它提供的服务。

(1)pom文件与上面类似,这次搭建的是服务提供者,因此引入eureka客户端依赖(spring-cloud-starter-eureka)就可以。

<?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.boe</groupId>
  <artifactId>Client01</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>Client01</name>
  <!-- FIXME change it to the project\'s website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <!--使用springboot的资源,结构特性。另外使用springcloud的所有依赖环境-->

  <!-- 基于springboot,继承springboot-parent,引入springboot的父pom文件 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
  </parent>

  <!--导入springcloud的父pom资源-->
  <dependencyManagement>
    <dependencies>
      <!--导入springcloud-->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Edgware.RELEASE</version>
        <!--注意通过坐标找到的spring-cloud-dependencies-Edgware.RELEASE.pom文件中,打包方式为pom,因此需要这里type为pom-->
        <type>pom</type>
        <scope>import</scope>
      </dependency>

    </dependencies>
  </dependencyManagement>

  <!--以上两步,完成继承父springboot pom依赖,并导入springcloud的依赖-->

  <dependencies>

    <!--引入eureka客户端依赖-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>

    <!-- spring-boot-starter-web 已经在eureka-server中传递,因此这里不需要配置springboot的starter-web-->

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!--springboot运行jar包的插件-->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>
View Code

(2)application.properties文件指定服务的端口号、服务名以及注册的中心地址url等信息。

# 客户端端口
server.port=9088

# 客户端通过ip底层注册
# eureka.client.prefer-ip-address=true
eureka.client.preferIpAddress=true

# eureka-client的注册,和发现功能需设置为true,默认为true,也可以不用设置
eureka.client.regist-with-eureka=true
# 发现抓取注册信息的内容
eureka.client.fetch-registry=true

# 定义服务名称
spring.application.name=service-hi


# 注册中心的地址
eureka.client.serviceUrl.defaultZone=http://localhost:8088/eureka

(3)写一个启动类,类似注册中心的启动类,只是注解需要修改成@EnableEurekaClient,这样的服务只有注册和发现的能力,不具备管理的能力。

package com;

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

/**
 * Eureka client服务提供者启动类
 */
@SpringBootApplication
@EnableEurekaClient
public class StarterEurekaClient {
    public static void main(String[] args) {
        SpringApplication.run(StarterEurekaClient.class,args);
        System.out.println("I am client01");
    }
}
View Code

(4)写个controller层的测试类,访问这个地址可以返回信息,如果能正常访问到,说明注册后的服务能正常使用。

package com.boe.Controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {
    //可以通过@Value注解,获取application.properties里的参数
    @Value("${server.port}")
    private String port;

    //写一个测试方法
    @RequestMapping("hello")
    public String test(String name){
        return "I am "+name+", and I come form "+port;
    }

}

(5)访问发现可以正常返回测试信息,并且注册中心页面也增添了一条注册信息,这样就实现了微服务的注册并提供服务的功能。

c.高可用eureka注册中心

高可用是分布式集群必不可少的一个特点,当其中一个节点宕机后,不影响整体的功能。eureka注册中心不仅对外可以提供服务注册,同时自己也作为注册微服务,可以注册在另外一台eureka注册中心,利用这个特点可以配置eureka高可用集群,这里配置3台eureka注册中心,实现集群HA服务。

eureka01注册到eureka02和eureka03,其他依次类推,这样完成注册后,每个eureka服务中心从对方抓得到的服务map信息就一致了,因为服务名一致,会纳入一个服务来管理,实现HA高可用。

想实现如上高可用,在IDEA当前工程下再添加两个Module,再修改application.properties文件 ,就可以实现,以eureka server01为例,需修改eureka.client.regist-with-eureka=true,使其具备注册的能力,同时修改eureka.client.fetch-registry=true,使其具备发现抓取注册的能力。另外eureka.client.serviceUrl.defaultZone写另外两台注册中心的地址,这样三台启动后就可以实现高可用。

# 端口
server.port=8088
# 客户端通过ip底层注册
# eureka.client.prefer-ip-address=true
eureka.client.preferIpAddress=true
#暂时先关闭,当前注册中心作为客户端
# 在注册中心本身,使得具有注册和发现的能力,如果要实现HA,需要改成true,这样就会访问注册中心的接口
eureka.client.regist-with-eureka=true
# 关闭发现抓取注册信息的内容
eureka.client.fetch-registry=true
# 注册中心的地址,http请求的接口,发现另外一个server的端口,实现HA高可用
eureka.client.serviceUrl.defaultZone=http://localhost:8089/eureka,http://localhost:8090/eureka

# 当前工程提供一个服务名称,相同功能的工程创建集群,都是一个名称,以当前服务名为key保存在当前服务的所有节点信息的map对象中
# 如果是同一个name,就可以创建HA高可用
spring.application.name=eureka-server

# 可以关闭自我保护,没有收到续约,就剔除服务
# eureka.server.enable-self-preservation=false

刚开始启动由于先后顺序,控制台会报错,这是正常的,因为先启动的服务可能找不到另外两台的(还在启动中),导致连接拒绝,以及找不到server的报错。

修改客户端的application.properties,添加另外两台注册中心的地址后,测试也可以访问到服务,停掉其中一台注册中心,发现依然可以使用client提供的服务,这样就实现了eureka注册中心的HA高可用。 

d.eureka注册信息的结构和相关机制

eureka注册信息的结构为一个双层map,上面注册中心的UI界面展示了部分map的内容,它由如下几部分组成。

(1)第一层key为服务名,即上面的eureka-server或者service-hi,这是在application.properties文件中通过spring.application.name定义的。第一层value为一个map,记录注册的实例信息。

(2)第二层key为固定结构,即上面的youngchaolinmac:eureka-server:8090等,由系统用户名:服务名:端口号组成,第二层value为注册的实例对象,包含它的详细信息,如ip地址、端口、注册和续约的事件戳等信息。

相关的机制,有注册、续约、服务剔除和保护机制等。

(1)注册:服务提供者,如上面的client,启动服务后会根据注册中心的地址,携带自己的信息,通过http协议访问注册中心,注册中心接收到服务提供者的信息后保存在注册中心。

(2)续约:服务提供者每隔30s,会给注册中心发送一个\'\'心跳\'\',告知自己的实例对象是存活的,否则注册中心会剔除死亡或不活跃的服务节点。

(3)剔除:注册中心每隔60s,会检查一次实例对象中的续约时间戳(保存在双层map中的第二层,在value中),如果超过90s没有续约,会将当前实例对象从第二层map中剔除,这样UI的Status下就会少一个实例对象。

(4)保护:集群的服务是很多的,会出现某个服务尽管续约了,但是由于网络或者通信的问题导致续约判断的失误从注册中心被剔除掉,如果大面积的出现这种情况会导致集群服务的瘫痪。为了避免这种情况的发生,springcloud提供了保护机制,即如果同一时间如果因为超时剔除的服务超过整体服务的15%以上,会被认为是非正常剔除,这样剔除不会实现,会保持集群服务原有的的状态。这种保护机制默认是开启的,也可以在application.properties中设置为关闭,即将eureka.server.enable-self-presevation=false来关闭。

ribbon

ribbon组件可以实现服务调服务, 需要引入RestTemplate来支持,它是一个客户端组件,会抓取在注册中心注册的服务信息,将其整理并保存,然后访问后端服务(上文的client)时会进行拦截,通过从spring容器中获取的负载均衡规则IRule对象,来实现对后端服务的访问逻辑。

为了实现ribbon的功能,需要提前准备2个client,上面已经准备了一个client,再需准备一个。另外还需要准备一个配置了ribbon的组件。

(1)准备一个新的client,当前工程下增添一个Module,修改端口号即可,其他无需修改,这里修改成了9089。

# 客户端端口
server.port=9089

# 客户端通过ip底层注册
# eureka.client.prefer-ip-address=true
eureka.client.preferIpAddress=true

# eureka-client的注册,和发现功能需设置为true,默认为true,也可以不用设置
eureka.client.regist-with-eureka=true
# 发现抓取注册信息的内容
eureka.client.fetch-registry=true

# 定义服务名称
spring.application.name=service-hi


# 注册中心的地址
eureka.client.serviceUrl.defaultZone=http://localhost:8088/eureka,http://localhost:8089/eureka,http://localhost:8090/eureka

(2)准备一个具有ribbon的服务组件,新建一个Module,注意pom文件需要添加ribbon的依赖,其他跟client的配置类似,另外ribbon服务组件的端口在application.properties文件中修改为8105,其他参考client的设置。

    <!--引入ribbon依赖-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-ribbon</artifactId>
    </dependency>

(3)ribbon服务组件,添加启动类,启动类中使用@Bean返回一个RestTemplate对象,交给spring来管理,因为启动类注解@SpringBootApplication底层有@SpringBootConfiguration,这个配置类的下面又是@Configuration,因此启动类也可以作为配置类来使用,在其中配置一个bean。

package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
 * 具有ribbon组件的服务启动类
 */
@SpringBootApplication
@EnableEurekaClient
public class StarterEurekaRibbon {
    public static void main(String[] args) {
        SpringApplication.run(StarterEurekaRibbon.class,args);
    }

    //使用restTemplate实现ribbon对客户端的拦截逻辑
    @Bean("restTemplate")
    @LoadBalanced //不加会访问不到其他微服务,提示找不到服务名,尽管有这个服务名的服务在运行着
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}

(4)ribbon服务组件添加controller层和service层,在service层实现对请求的拦截,service层方法访问的是一个服务名,而不是具体的请求地址。因为这个服务能获取到注册中心其他服务的信息,将client对应的注册信息抓取过来,通过service-hi,就能获取到对应的双层map的第二层map的value,通过value里保存的ip、端口等信息,实现对真实服务的访问。

 controller层

package com.boe.Controller;

import com.boe.Service.RibbonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RibbonController {

    @Autowired
    RibbonService ribbonService=null;

    //访问ribbon服务的地址
    @RequestMapping("helloribbon")
    public String test(String name){
        return "Ribbon work, "+ribbonService.hello(name);
    }
}
View Code

service层

package com.boe.Service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class RibbonService {

    @Autowired
    RestTemplate restTemplate=null;

    //需要调用RestTemplate,通过服务名来实现对服务的访问,默认轮询
    public String hello(String name) {
        return restTemplate.getForObject("http://service-hi/hello?name="+name,String.class);
    }
}
View Code

(5)最后启动新添的client服务和有ribbon组件的服务,启动后正常均在注册中心注册。

(6)通过有ribbon组件的服务来访问client的服务,实现服务调服务,默认情况下是轮询的方式,当我多次访问如下地址后,会轮流出现9088和9089,说明访问的服务是轮流来的,也可以根据需求修改成其他的策略,需要在启动类中再添加如下代码。

    //自定义负载均衡-随机,还有其他的选择
    //需要配置bean,交给spring管理,如果ribbon组件在启动时会检测这个bean是否存在
    //如果不存在,就默认轮询
    @Bean
    public IRule initRandom(){
        return new RandomRule();//返回随机
        //return new RoundRobinRule();//返回负载轮询方式
        //return WeightedResponseTimeRule();//根据响应时间,返回对应大小权重值的方式,响应快权重值高,响应慢则权重值低
    }

访问有ribbon组

以上是关于SpringCloud入门搭建及服务调用的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud 入门 -- 搭建Eureka注册中心 实现服务者与消费者的服务调用

Spring Cloud 入门 -- 搭建Eureka注册中心 实现服务者与消费者的服务调用

Spring Cloud 入门 -- 搭建Eureka注册中心 实现服务者与消费者的服务调用

Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)SpringBoot项目实现商品服务器端是调用

Springcloud使用入门

SpringCloud 入门理论知识