一、基本组件

  • 注册中心:Eureka
  • 负载均衡:Ribbon
  • 声明式调用远程方法:Feign
  • 熔断、降级、监控:Hystrix
  • 网关:Zuul

SpringCloud基本组件

二、基础测试环境搭建

使用 RestTemplate 实现远程方法调用

1、结构

SpringCloud测试环境结构

2、具体搭建

2.1、创建 parent 父工程

pro01-spring-cloud-parent,其打包方式为pom
在pom.xml文件中加入依赖

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.tianfei</groupId>
    <artifactId>pro01-spring-cloud-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>


    <!-- 配置依赖管理 -->
    <dependencyManagement>
        <dependencies> 
        <!-- 导入 SpringCloud 需要使用的依赖信息 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type> 
                <!-- import 依赖范围表示将 spring-cloud-dependencies 包中的依赖信息导入 -->
                <scope>import</scope>
            </dependency> 
            <!-- 导入 SpringBoot 需要使用的依赖信息 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.2、创建 coomon 通用工程

pro02-spring-cloud-common,其打包方式为jar
① pom.xml文件配置如下:

  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.tianfei</groupId>
    <artifactId>pro01-spring-cloud-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>pro02-spring-cloud-common</artifactId>
  <packaging>jar</packaging>

② 在该工程中创建实体类 Employee

/**
 * @author tianfei
 *
 */
public class Employee {

    private Integer empId;
    private String empName;
    private Double empSalary;
    
    public Employee() {
    }

    public Employee(Integer empId, String empName, Double empSalary) {
        super();
        this.empId = empId;
        this.empName = empName;
        this.empSalary = empSalary;
    }

    public Integer getEmpId() {
        return empId;
    }

    public void setEmpId(Integer empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public Double getEmpSalary() {
        return empSalary;
    }

    public void setEmpSalary(Double empSalary) {
        this.empSalary = empSalary;
    }

    @Override
    public String toString() {
        return "Employee [empId=" + empId + ", empName=" + empName + ", empSalary=" + empSalary + "]";
    }
    
}

2.3、创建 provider 提供者工程

pro03-spring-cloud-provider,其打包方式为jar
① pom.xml配置文件如下

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.tianfei</groupId>
        <artifactId>pro01-spring-cloud-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>pro03-spring-cloud-provider</artifactId>
    <packaging>jar</packaging>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.tianfei</groupId>
            <artifactId>pro02-spring-cloud-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

② 创建启动类 SpringCloudProvider

@SpringBootApplication
public class SpringCloudProvider {

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

③ 在 Resource 目录下 创建 application.yml 文件并做如下配置

server:
  port: 1000

④ 创建 Controller

由于服务端 响应返回的都是JSON数据,所以使用 @RestController

@RestController
public class EmployeeController {
    
    @RequestMapping("/provider/get/employee/remote")
    public Employee getEmployeeRemote() {
        return new Employee(555, "tom555", 555.55);
    }
}

⑤ 启动并试着访问该请求
Provider端请求放回的JSON数据

2.4、创建 consumer 消费者工程

pro04-spring-cloud-consumer,其打包方式为jar
① pom.xml配置如下

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.tianfei</groupId>
        <artifactId>pro01-spring-cloud-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>pro04-spring-cloud-consumer</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.tianfei</groupId>
            <artifactId>pro02-spring-cloud-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

② 创建启动类 SpringCloudConsumer

@SpringBootApplication
public class SpringCloudConsumer {

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

③ 创建 application.yml 文件,配置端口号

server:
  port: 4000

④ 创建 配置类,用来将 RestTemplate 注入到 IOC容器中

@Configuration
public class SpringCloudConfig {

    @Bean
    public RestTemplate getRestTemplate(){

        return new RestTemplate();
    }
}

⑤ 编写 Controller

/**
 * @author: Herz
 * @date: 2021/7/24 10:30
 */
@RestController
public class HumanResourceController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/get/employee")
    public Employee getEmployeeRemote() {

        // 1、声明远程微服务的主机地址加端口号
        String host = "http://localhost:1000";

        // 2、声明具体要调用的功能的URL地址
        String url = "/provider/get/employee/remote";

        // 3、通过 RestTemplate 调用远程微服务
        return restTemplate.getForObject(host + url, Employee.class);
    }
}

⑥ 试着从 Consumer 端远程取得 Provider 端返回的数据
Consumer端取得JSON数据

2.5、创建 Eureka 注册中心

pro05-spring-cloud-eureka
① pom.xml 配置文件如下

    <parent>
        <artifactId>pro01-spring-cloud-parent</artifactId>
        <groupId>com.tianfei</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>pro05-spring-cloud-eureka</artifactId>

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

如果在启动该工程的启动类时,报错 Caused by: java.lang.IllegalStateException: StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[] failed to start 时,在pom.xml 中加入以下依赖

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>

        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.2</version>
        </dependency>

        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>

② 创建启动类 SpringCloudEureka

// 启用 Eureka 服务器功能
@EnableEurekaServer

@SpringBootApplication
public class SpringCloudEureka {

    public  static void main(String[] args) {

        SpringApplication.run(SpringCloudEureka.class, args);
    }

}

③ 创建 application.yml 文件,并做如下配置

server:
  port: 5000

eureka:
  instance:
    hostname: localhost                 # 配置当前 Eureka 服务的主机地址
  client:
    register-with-eureka: false         # 当前服务本身就是注册中心,不必“自己注册自己”
    fetch-registry: false               # 当前服务本身就是注册中心,不必“从注册中心取回信息”
    service-url:                        # 客户端(指的是consumer,provider)访问当前注册中心的地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

④ 启动并尝试访问 Eureka 界面
Eureka界面

2.6、目标1:将 Provider 注册到 Eureka

① 在 pro03-spring-cloud-provider 工程的 pom.xml文件中加入如下依赖:

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

② 在 pro03-spring-cloud-provider 工程的 yml 配置文件中,加入如下配置

server:
  port: 1000


eureka:
  client:
    service-url:
      defaultZone: http://localhost:5000/eureka/     # provider 作为客户端访问 Eureka 的访问地址(所以需要加上“/eureka”)

spring:
  application:
    name: com-provider                                # 设置当前微服务的应用名称,以便将来通过微服务名称调用当前微服务时能够找到
                                                      # 在 SpringCloud 环境下开发,每一个微服务工程都要设置一个应用名称

③ 重新启动 Provider 工程的启动类
④ 刷新 Eureka 页面查看是否注册成功
Provider注册成功

2.7、目标2:consumer 访问 provider 时使用微 服务名称代替 localhost:1000

① 分析
服务名如何代替 ip:port
② 在 Consumer 工程的 pom.xml中添加如下依赖:

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

③ 在 application.yml 配置文件中再次添加:

spring:
  application:
    name: com-consumer        # 指定微服务的应用名

eureka:
  client:
    service-url:
      defaultZone: http://localhost:5000/eureka/       # 使用 eureka 注册中心找到要相应被调用的微服务的信息

④ 在注入 RestTemplate 的 Bean 时,加上 @LoadBalance 支持负载均衡

@Configuration
public class SpringCloudConfig {

    // 支持负载均衡
    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate(){

        return new RestTemplate();
    }
}

⑤ 在 Controller 里将 微服务应用名 代替 主机地址和端口号

/**
 * @author: Herz
 * @date: 2021/7/24 10:30
 */
@RestController
public class HumanResourceController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/get/employee")
    public Employee getEmployeeRemote() {

        // 1、声明远程微服务的主机地址加端口号
//        String host = "http://localhost:1000";
        // 使用 provider 的 “微服务名称” 代替 “主机地址和端口号”
        String host = "http://com-provider";

        // 2、声明具体要调用的功能的URL地址
        String url = "/provider/get/employee/remote";

        // 3、通过 RestTemplate 调用远程微服务
        return restTemplate.getForObject(host + url, Employee.class);
    }
}

2.8、目标3:使用 Ribbon 的客户端负载均衡功能

1、分析
Ribbon负载均衡的实现
2、引入 Ribbon 依赖,由于 目标2 已经在 Consumer 工程中引入该依赖,可不再引入
3、修改 Provider 工程中得 Controller 方法

@RestController
public class EmployeeController {

    @RequestMapping("/provider/get/employee/remote")
    public Employee getEmployeeRemote(HttpServletRequest request) {

        // 获取当前微服务的端口号
        int serverPort = request.getServerPort();

        return new Employee(555, "tom555 " + serverPort, 555.55);
    }
}

4、Provider 以集群的方式启动
按照端口号 1000 启动第一个实例
按照端口号 2000 启动第二个实例
按照端口号 3000 启动第三个实例

在Provider工程的application.yml中改动端口号即可

server:
  port: 3000   # 改动这里即可

5、Consumer 正常访问
端口号:1000的Provider
端口号:2000的Provider
端口号:3000的Provider
每次刷新时,将依此顺序交替,体现了负载均衡的轮询策略。即 Ribbon 的负载均衡功能已起作用。

!!!注意: provider 的微服务名称必须使用同一个名称才能构成一个集群,否则将不会认定为是属于同一个集群。

使用 Feign 实现远程方法调用

1、分析

Feign的用法

2、操作

1、在 Common 工程的pom.xml中引入依赖

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

2、 在 Common 工程中创建远程调用方法的接口
!!!注意: 该接口所在的包需要和其他工程的启动类所在的包名一致,或者是其子包。

// @FeignClient 注解表示当前接口和一个Provider对应,注解中得value属性指定要调用的Provider的微服务名称
@FeignClient( value = "com-provider")
public interface EmployeeRemoteService {

    // 远程调用的接口方法
    // 要求 @RequestMapping 注解中得地址 和 Provider 中得一致
    // 要求方法声明一致
    // 用来获取请求参数的 @RequestParam,@PathVariable,@RequestBody 不能省略,两边一致
    @RequestMapping("/provider/get/employee/remote")
    public Employee getEmployeeRemote();
}

3、修改 Provider 工程中得 Controller

@RestController
public class EmployeeController {

    @RequestMapping("/provider/get/employee/remote")
    public Employee getEmployeeRemote() {

        return new Employee(555, "tom555 ", 555.55);
    }
}

4、创建新的 Consumer 工程 pro04-spring-cloud-fegin-consumer
① 在pom.xml中加入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.tianfei</groupId>
            <artifactId>pro02-spring-cloud-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>


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

由于 Common 工程中已经添加了 Feign 依赖,而 Feign 依赖已经包含 Ribbon 的依赖,所以根据依赖的传递性,在 Consumer 工程中不用再添加 Ribbon 的依赖

② 添加启动类 SpringCloudFeignConsumer

// 启用客户端功能
@EnableFeignClients
@SpringBootApplication
public class SpringCloudFeignConsumer {

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

③ 创建 新的 Consumer 工程的 Controller

@RestController
public class FeignHumanResourceController {

    // 装配调用远程微服务的接口,后面像调用本地方法一样使用
    @Autowired
    EmployeeRemoteService employeeRemoteService;

    @RequestMapping("/feign/consumer/get/employee")
    public Employee getRemoteEmployee(){

        return employeeRemoteService.getEmployeeRemote();
    }
}

④ 在 resource 目录创建 application.yml 配置文件并做以下配置

server:
  port: 6000

spring:
  application:
    name: com-feign-consumer

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:5000/eureka/

⑤ 访问页面获取返回的数据
使用Feign远程调用接口成功

3、传参

1、简单类型
① common 的接口的写法

// @FeignClient 注解表示当前接口和一个Provider对应,注解中得value属性指定要调用的Provider的微服务名称
@FeignClient( value = "com-provider")
public interface EmployeeRemoteService {

        @RequestMapping("/provider/get/employee/by/id") 
        public Employee getEmployeeById(@RequestParam("empId") Integer empId);
}

别忘了写@RequestParam 注解

②provider 的 controller 方法:

@RestController
public class EmployeeController {

        @RequestMapping("/provider/get/employee/by/id") 
        public Employee getEmployeeById(@RequestParam("empId") Integer empId) { 
                return new Employee(empId, "tom999", 999.99); 
        }
}

方法声明部分需和接口中一致

2、复杂类型
① common 中得接口的写法

// @FeignClient 注解表示当前接口和一个Provider对应,注解中得value属性指定要调用的Provider的微服务名称
@FeignClient( value = "com-provider")
public interface EmployeeRemoteService {

        @RequestMapping("/provider/save/emp") 
        public Employee saveEmp(@RequestBody Employee employee);
}

别忘了写@RequestBody 注解

②provider 的 controller 方法:

@RestController
public class EmployeeController {

        @RequestMapping("/provider/save/emp") 
        public Employee saveEmp(@RequestBody Employee employee) {
                 return employee; 
         }
}

方法声明部分需和接口中一致

最后修改:2021 年 07 月 28 日 09 : 15 AM
如果觉得我的文章对你有用,请随意赞赏