Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。本文主要介绍Spring Cloud的基本使用,文中使用到的软件版本:Spring Boot 2.2.5.RELEASE、Spring Cloud Hoxton.SR3、Java 1.8.0_191。

1、项目结构

使用maven来管理工程,工程里包含多个模块:注册中心(scdemo-eureka)、服务提供者(scdemo-server)、服务调用者(scdemo-client)、网关(scdemo-gateway),差不多下面的样子:

SpringCloud入门实战(1)-Hello World_xml

2、根工程(scdemo)

跟功能只有一个pom文件,用于定义Spring Boot、Spring Cloud及其他一些框架的版本信息。

scdemo/pom.xml

<?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.inspur</groupId>
    <artifactId>scdemo</artifactId>
    <version>1.0</version>
    <modules>
        <module>scdemo-eureka</module>
        <module>scdemo-srever</module>
        <module>scdemo-client</module>
        <module>scdemo-gateway</module>
    </modules>
    <packaging>pom</packaging>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

3、注册中心(scdemo-eureka)

3.1、scdemo/scdemo-eureka/pom.xml

<?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">
    <parent>
        <artifactId>scdemo</artifactId>
        <groupId>com.inspur</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>scdemo-eureka</artifactId>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.2、application.yml

spring:
  application:
    name: scdemo-eureka

server:
  port: 9000

eureka:
  instance:
    prefer-ip-address: true
    #hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:${server.port}/eureka
  server:
    enable-self-preservation: false

3.3、启动类

package com.inspur.scdemo.eureka;

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

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

3.4、控制台地址

启动后,控制台地址为:http://localhost:9000/

4、服务提供者(scdemo-server)

4.1、scdemo/scdemo-server/pom.xml

<?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">
    <parent>
        <artifactId>scdemo</artifactId>
        <groupId>com.inspur</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>scdemo-srever</artifactId>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

4.2、application.yml

spring:
  application:
    name: scdemo-server
  cloud:
    loadbalancer:
      ribbon:
        enabled: false
server:
  port: 9001
  servlet:
    context-path: /
eureka:
  instance:
    prefer-ip-address: true
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:9000/eureka/

4.3、启动类

package com.inspur.scdemo.server;

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

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

4.4、编写Controller

package com.inspur.scdemo.server.controller;

import com.inspur.scdemo.server.entity.CallResult;
import com.inspur.scdemo.server.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {
    private Logger logger = LoggerFactory.getLogger(UserController.class);
    
    @RequestMapping("/addUser")
    public CallResult<User> addUser(@RequestBody User user) {
        logger.info(user.toString());
        //TODO: insert to db
        user.setId(1000L);
        user.setName("server-" + user.getName());
        CallResult<User> result = new CallResult<>();
        result.setResult(user);
        return result;
    }
    
    @RequestMapping("/getUser")
    public CallResult<User> getUser(long id) {
        logger.info(id + "");
        //TODO: select from db
        User user = new User();
        user.setId(id);
        user.setName("server-马云");
        user.setAddress("杭州");
        CallResult<User> result = new CallResult<User>();
        result.setResult(user);
        return result;
    }
}

5、服务调用者(scdemo-client)

5.1、scdemo/scdemo-client/pom.xml

<?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">
    <parent>
        <artifactId>scdemo</artifactId>
        <groupId>com.inspur</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>scdemo-client</artifactId>

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

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

5.2、application.yml

spring:
  application:
    name: scdemo-client
  cloud:
    loadbalancer:
      ribbon:
        enabled: false
server:
  port: 9002
  servlet:
    context-path: /
eureka:
  instance:
    prefer-ip-address: true
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:9000/eureka

feign:
  hystrix:
    enabled: true

5.3、启动类

package com.inspur.scdemo.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

5.4、编写Feign接口调用服务端

package com.inspur.scdemo.client.feign;

import com.inspur.scdemo.client.entity.CallResult;
import com.inspur.scdemo.client.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "scdemo-server")
public interface IUserFeign {
    @PostMapping("/user/addUser")
    CallResult<User> addUser(@RequestBody User user);

    @PostMapping("/user/getUser")
    CallResult<User> getUser(@RequestParam("id") long id);
}

5.5、编写Controller

package com.inspur.scdemo.client.controller;

import com.inspur.scdemo.client.entity.CallResult;
import com.inspur.scdemo.client.entity.User;
import com.inspur.scdemo.client.feign.IUserFeign;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/user")
public class UserController {
    private Logger logger = LoggerFactory.getLogger(UserController.class);
    @Autowired
    private IUserFeign userFeign;
    
    @RequestMapping("/addUser")
    public CallResult<User> addUser(String name, String address) {
        logger.info("name={},address={}", name, address);
        CallResult<User> result = new CallResult<>();
        try {
            User user = new User();
            user.setName(name);
            user.setAddress(address);
            CallResult<User> resultServer = userFeign.addUser(user);
            logger.info("server返回结果:{}" + resultServer);

            User user2 = resultServer.getResult();
            user2.setName("client-" + user2.getName());
            result.setResult(user2);
        } catch (Exception e) {
            result = new CallResult<User>(-1, "发生异常");
            e.printStackTrace();
        }
        return result;
    }
    
    @RequestMapping("/getUser")
    public CallResult<User> getUser(long id) {
        logger.info(id + "");
        
        CallResult<User> result = null;
        try {
            result = userFeign.getUser(id);
        } catch (Exception e) {
            result = new CallResult<User>(-1, "发生异常");
            e.printStackTrace();
        }
        return result;
    }
}

6、网关(scdemo-gateway)

6.1、scdemo/scdemo-gateway/pom.xml

<?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">
    <parent>
        <artifactId>scdemo</artifactId>
        <groupId>com.inspur</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

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

6.2、application.yml

spring:
  application:
    name: scdemo-gateway
  cloud:
    loadbalancer:
      ribbon:
        enabled: false
    gateway:
      discovery:
        locator:
          enabled: false #是否开启服务注册和发现功能;将为每一个服务创建一个router,这个router把以服务名开头的请求路径转发到对应的服务上
          lower-case-service-id: true #将请求路径上的服务名转为小写
      routes:
        - id: client
          uri: lb://scdemo-client
          predicates:
            - Path=/scdemo-client/**
          filters:
            - StripPrefix=1 #路由时会去除/scdemo-client

server:
  port: 9006
  servlet:
    context-path: /
eureka:
  instance:
    prefer-ip-address: true
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:9000/eureka/

6.3、启动类

package com.inspur.scdemo.gateway;

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

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

7、测试

分别启动各个模块。

7.1、查看注册的服务

 打开http://localhost:9000/,可以看到注册的服务:

SpringCloud入门实战(1)-Hello World_Spring Cloud_02

7.2、访问服务调用者的Controller

打开http://localhost:9002/user/addUser?name=a&address=b

可以看到服务调用者的后台日志,成功调用了服务提供者:

SpringCloud入门实战(1)-Hello World_spring_03

 页面也返回正确结果:

SpringCloud入门实战(1)-Hello World_Spring Cloud_04

7.3、通过网关访问服务调用者的Controller

打开http://localhost:9006/scdemo-client/user/addUser?name=a&address=b,页面也返回正确结果:

SpringCloud入门实战(1)-Hello World_maven_05