SpringCloud简介
Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线)。分布式系统的协调导致了样板模式, 使用Spring Cloud开发人员可以快速地支持实现这些模式的服务和应用程序。他们将在任何分布式环境中运行良好,包括开发人员自己的笔记本电脑,裸机数据中心,以及Cloud Foundry等托管平台。
官网:https://spring.io/projects/spring-cloud
特点
Spring Cloud专注于为典型的用例和可扩展性机制(包括其他用例)提供良好的开箱即用体验。
-
分布式/版本化配置
-
服务注册和发现
-
路由
-
服务到服务的呼叫
-
负载均衡
-
断路器
-
全局锁
-
领导选举和集群状态
-
分布式消息传递
Spring Cloud 架构图
SpringCloud项目搭建
学习SpringCloud先要有,SpringBoot的相关知识,参考【SpringBoot】SpringBoot快速入门(一)
SpringBoot 是基于 SpringFramework 来构建的,SpringFramework 是一种 J2EE 的框架,SpringBoot 是一种快速构建 Spring 应用,SpringCloud 是构建 Spring Boot 分布式环境,也就是常说的云应用,SpringBoot 中流砥柱,承上启下
本例项目架构
环境准备
1)JDK 环境必须是 1.8 及以上,本例使用版本:1.8.0_181
2)Maven 项目管理工具,3.6.0及以上版本,本例使用版本:3.6.3
3)开发工具使用 IDEA
搭建项目父工程
1、新建一个maven空工程test-springcloud
2、编写pom文件,引入项目所需要依赖的包
本例使用的SpringBoot版本是2.2.5.RELEASE,SpringCloud版本是Hoxton.SR3
使用其他版本,需要注意SpringBoot和SpringCloud版本的兼容问题
1 <!-- spring boot --> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-dependencies</artifactId> 5 <version>2.2.5.RELEASE</version> 6 <type>pom</type> 7 <scope>import</scope> 8 </dependency> 9 <!-- spring cloud --> 10 <dependency> 11 <groupId>org.springframework.cloud</groupId> 12 <artifactId>spring-cloud-dependencies</artifactId> 13 <version>Hoxton.SR3</version> 14 <type>pom</type> 15 <scope>import</scope> 16 </dependency>
完整pom文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>com.test</groupId> 8 <artifactId>test-springcloud</artifactId> 9 <version>1.0-SNAPSHOT</version> 10 <packaging>pom</packaging> 11 12 13 <modules> 14 <module>test-springcloud-eureka-server8761</module> 15 <module>test-springcloud-order8000</module> 16 <module>test-springcloud-provider-payment8001</module> 17 </modules> 18 19 20 <!-- 统一管理jar包版本 --> 21 <properties> 22 <spring-boot.version>2.2.5.RELEASE</spring-boot.version> 23 <spring-cloud.version>Hoxton.SR3</spring-cloud.version> 24 <!-- <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>--> 25 <mybatis-spring-boot.version>2.1.2</mybatis-spring-boot.version> 26 <mysql.version>8.0.12</mysql.version> 27 <druid.version>1.1.21</druid.version> 28 <lombok.version>1.18.12</lombok.version> 29 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 30 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 31 <java.version>1.8</java.version> 32 </properties> 33 34 35 <!-- 依赖管理:子模块继承后,提供作用:锁定版本 + 子module 不用写 version --> 36 <dependencyManagement> 37 <dependencies> 38 <!-- spring boot --> 39 <dependency> 40 <groupId>org.springframework.boot</groupId> 41 <artifactId>spring-boot-dependencies</artifactId> 42 <version>${spring-boot.version}</version> 43 <type>pom</type> 44 <scope>import</scope> 45 </dependency> 46 <!-- spring cloud --> 47 <dependency> 48 <groupId>org.springframework.cloud</groupId> 49 <artifactId>spring-cloud-dependencies</artifactId> 50 <version>${spring-cloud.version}</version> 51 <type>pom</type> 52 <scope>import</scope> 53 </dependency> 54 <!-- spring cloud alibaba --> 55 <!-- <dependency>--> 56 <!-- <groupId>com.alibaba.cloud</groupId>--> 57 <!-- <artifactId>spring-cloud-alibaba-dependencies</artifactId>--> 58 <!-- <version>${spring-cloud-alibaba.version}</version>--> 59 <!-- <type>pom</type>--> 60 <!-- <scope>import</scope>--> 61 <!-- </dependency>--> 62 63 <!-- mybatis --> 64 <dependency> 65 <groupId>org.mybatis.spring.boot</groupId> 66 <artifactId>mybatis-spring-boot-starter</artifactId> 67 <version>${mybatis-spring-boot.version}</version> 68 </dependency> 69 70 <!-- mysql --> 71 <dependency> 72 <groupId>mysql</groupId> 73 <artifactId>mysql-connector-java</artifactId> 74 <version>${mysql.version}</version> 75 </dependency> 76 77 <!-- druid --> 78 <dependency> 79 <groupId>com.alibaba</groupId> 80 <artifactId>druid-spring-boot-starter</artifactId> 81 <version>${druid.version}</version> 82 </dependency> 83 84 <!-- lombok --> 85 <dependency> 86 <groupId>org.projectlombok</groupId> 87 <artifactId>lombok</artifactId> 88 <version>${lombok.version}</version> 89 </dependency> 90 91 <!-- test --> 92 <dependency> 93 <groupId>org.springframework.boot</groupId> 94 <artifactId>spring-boot-starter-test</artifactId> 95 <version>${spring-boot.version}</version> 96 <scope>test</scope> 97 <exclusions> 98 <exclusion> 99 <groupId>org.junit.vintage</groupId> 100 <artifactId>junit-vintage-engine</artifactId> 101 </exclusion> 102 </exclusions> 103 </dependency> 104 </dependencies> 105 </dependencyManagement> 106 107 <!-- SpringBoot打包插件,可以将代码打包成一个可执行的jar包 --> 108 <build> 109 <finalName>${project.artifactId}</finalName> 110 <plugins> 111 <plugin> 112 <groupId>org.apache.maven.plugins</groupId> 113 <artifactId>maven-compiler-plugin</artifactId> 114 <configuration> 115 <source>${java.version}</source> 116 <target>${java.version}</target> 117 </configuration> 118 </plugin> 119 <plugin> 120 <groupId>org.springframework.boot</groupId> 121 <artifactId>spring-boot-maven-plugin</artifactId> 122 <version>${spring-boot.version}</version> 123 <configuration> 124 <fork>true</fork> 125 <addResources>true</addResources> 126 </configuration> 127 </plugin> 128 </plugins> 129 </build> 130 </project> 131 132 pom.xml
3、项目结构图如下:
搭建Eureka注册中心
即Eureka的服务端
1、在父项目上,new module 新建一个模块Eureka注册中心,及Eureka服务端 test-springboot-eureka-server8761
2、编写pom.xml文件
Eureka服务端依赖如下:
1 <!-- eureka server --> 2 <dependency> 3 <groupId>org.springframework.cloud</groupId> 4 <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> 5 </dependency>
完整pom.xml文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>test-springcloud</artifactId> 7 <groupId>com.test</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>test-springcloud-eureka-server8761</artifactId> 13 14 <dependencies> 15 <!-- eureka server --> 16 <dependency> 17 <groupId>org.springframework.cloud</groupId> 18 <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> 19 </dependency> 20 21 <!-- spring boot --> 22 <dependency> 23 <groupId>org.springframework.boot</groupId> 24 <artifactId>spring-boot-starter-web</artifactId> 25 </dependency> 26 <dependency> 27 <groupId>org.springframework.boot</groupId> 28 <artifactId>spring-boot-starter-actuator</artifactId> 29 </dependency> 30 <dependency> 31 <groupId>org.springframework.boot</groupId> 32 <artifactId>spring-boot-devtools</artifactId> 33 <scope>runtime</scope> 34 <optional>true</optional> 35 </dependency> 36 37 <dependency> 38 <groupId>org.springframework.boot</groupId> 39 <artifactId>spring-boot-starter-test</artifactId> 40 <scope>test</scope> 41 </dependency> 42 43 </dependencies> 44 45 <build> 46 <finalName>test-springcloud-eureka-server8761</finalName> 47 </build> 48 </project>
3、编写application.yml配置文件
1 # 端口 2 server: 3 port: 8761 4 5 spring: 6 application: 7 name: cloud-eureka-server 8 9 # Eureka配置 10 eureka: 11 instance: 12 # eureka服务端的实例名称 13 hostname: localhost 14 client: 15 # false表示不向注册中心注册自己 16 register-with-eureka: false 17 # false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检查服务 18 fetch-registry: false 19 # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址 20 service-url: 21 defaultZone: http://localhost:8761/eureka
4、编辑SpringBoot启动类EurekaMain8761.java,并使用@EnableEurekaServer
1 // Eureka服务端 2 @EnableEurekaServer 3 @SpringBootApplication 4 public class EurekaMain8761 { 5 public static void main(String[] args) { 6 SpringApplication.run(EurekaMain8761.class, args); 7 } 8 }
5、启动test-springboot-eureka-server8761项目,使用地址:http://localhost:8761/,进行访问
搭建支付模块、服务提供者
即Eureka客户端
需要准备一个test_springcloud的mysql数据库,新建一张表payment,内容如下:
1 SET NAMES utf8mb4; 2 SET FOREIGN_KEY_CHECKS = 0; 3 4 -- ---------------------------- 5 -- Table structure for payment 6 -- ---------------------------- 7 DROP TABLE IF EXISTS `payment`; 8 CREATE TABLE `payment` ( 9 `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', 10 `serial` varchar(255) DEFAULT '' COMMENT '流水号', 11 PRIMARY KEY (`id`) 12 ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; 13 14 -- ---------------------------- 15 -- Records of payment 16 -- ---------------------------- 17 BEGIN; 18 INSERT INTO `payment` VALUES (1, 'No.1'); 19 INSERT INTO `payment` VALUES (2, 'No.2'); 20 INSERT INTO `payment` VALUES (3, 'No.3'); 21 COMMIT; 22 23 SET FOREIGN_KEY_CHECKS = 1;
1、在父项目上,new module 新建一个模块支付模块,即Eureka客户端 test-springcloud-provider-payment8001
2、编写payment模块的pom.xml文件,引入Eureka 客户端依赖
1 <!-- eureka client --> 2 <dependency> 3 <groupId>org.springframework.cloud</groupId> 4 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 5 </dependency>
完整pom.xml文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>test-springcloud</artifactId> 7 <groupId>com.test</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>test-springcloud-provider-payment8001</artifactId> 13 14 <dependencies> 15 16 <!-- eureka client --> 17 <dependency> 18 <groupId>org.springframework.cloud</groupId> 19 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 20 </dependency> 21 22 <!-- spring boot --> 23 <dependency> 24 <groupId>org.springframework.boot</groupId> 25 <artifactId>spring-boot-starter-web</artifactId> 26 </dependency> 27 <dependency> 28 <groupId>org.springframework.boot</groupId> 29 <artifactId>spring-boot-starter-actuator</artifactId> 30 </dependency> 31 <dependency> 32 <groupId>org.springframework.boot</groupId> 33 <artifactId>spring-boot-starter-jdbc</artifactId> 34 </dependency> 35 <dependency> 36 <groupId>org.springframework.boot</groupId> 37 <artifactId>spring-boot-devtools</artifactId> 38 <scope>runtime</scope> 39 <optional>true</optional> 40 </dependency> 41 <dependency> 42 <groupId>org.mybatis.spring.boot</groupId> 43 <artifactId>mybatis-spring-boot-starter</artifactId> 44 </dependency> 45 <dependency> 46 <groupId>org.projectlombok</groupId> 47 <artifactId>lombok</artifactId> 48 <optional>true</optional> 49 </dependency> 50 <!-- mysql --> 51 <dependency> 52 <groupId>mysql</groupId> 53 <artifactId>mysql-connector-java</artifactId> 54 </dependency> 55 <!-- druid --> 56 <dependency> 57 <groupId>com.alibaba</groupId> 58 <artifactId>druid-spring-boot-starter</artifactId> 59 </dependency> 60 <dependency> 61 <groupId>org.springframework.boot</groupId> 62 <artifactId>spring-boot-starter-test</artifactId> 63 <scope>test</scope> 64 </dependency> 65 66 </dependencies> 67 68 <build> 69 <finalName>test-springcloud-provider-payment8001</finalName> 70 </build> 71 </project>
3、编辑application.yml配置文件
1 # 端口 2 server: 3 port: 8001 4 5 spring: 6 application: 7 name: cloud-payment-service 8 # 数据源基本配置 9 datasource: 10 driver-class-name: com.mysql.cj.jdbc.Driver 11 url: jdbc:mysql://localhost:3306/test_springcloud?allowPublicKeyRetrieval=true&useSSL=true 12 username: admin 13 password: 123456 14 15 eureka: 16 client: 17 # 表示将自己注册进Eureka Server默认为true 18 register-with-eureka: true 19 # 是否从Eureka Server抓去已有的注册信息,默认是true 20 fetch-registry: true 21 # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址 22 service-url: 23 defaultZone: http://localhost:8761/eureka 24 25 mybatis: 26 mapperLocations: classpath:mapper/*Mapper.xml 27 # 所有entity别名类所在的包 28 type-aliases-pachage: com.test.springcloud.entities
4、编写启动类
1 // Eureka客户端 2 @EnableEurekaClient 3 @SpringBootApplication 4 public class PaymentMain8001 { 5 public static void main(String[] args) { 6 SpringApplication.run(PaymentMain8001.class, args); 7 } 8 }
5、新建一个实体类Payment.java
1 @Data 2 @AllArgsConstructor 3 @NoArgsConstructor 4 public class Payment { 5 private Long id; 6 private String serial; 7 }
6、新建一个实体类映射文件 src/main/resources/mapper/PaymentMapper.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 3 4 <mapper namespace="com.test.springcloud.dao.PaymentDao"> 5 6 <resultMap id="BaseResultMap" type="com.test.springcloud.entities.Payment" > 7 <id property="id" jdbcType="BIGINT" column="id" /> 8 <result property="serial" jdbcType="VARCHAR" column="serial" /> 9 </resultMap> 10 11 <insert id="insert" parameterType="com.test.springcloud.entities.Payment" useGeneratedKeys="true" keyProperty="id"> 12 INSERT INTO payment(serial) values(#{serial}) 13 </insert> 14 15 <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap" > 16 SELECT * FROM payment WHERE id = #{id} 17 </select> 18 </mapper>
7、新建一个dao接口
1 @Mapper 2 public interface PaymentDao { 3 // 插入 4 public int insert(Payment payment); 5 // 获取 6 public Payment getPaymentById(@Param("id") Long id); 7 }
8、新建一个service接口
1 public interface PaymentService { 2 // 插入 3 public int insert(Payment payment); 4 // 获取 5 public Payment getPaymentById(Long id); 6 }
9、编写一个接口实现类
1 @Service 2 public class PaymentServiceImpl implements PaymentService { 3 4 @Autowired 5 private PaymentDao paymentDao; 6 7 public int insert(Payment payment) { 8 return paymentDao.insert(payment); 9 } 10 11 public Payment getPaymentById(Long id) { 12 return paymentDao.getPaymentById(id); 13 } 14 }
10、编辑一个通用结果类CommonResult.java
1 /** 2 * 通用结果 3 * @param <T> 4 */ 5 @Data 6 @AllArgsConstructor 7 @NoArgsConstructor 8 public class CommonResult<T> { 9 10 private int code; 11 private String msg; 12 private T data; 13 14 public CommonResult(int code, String msg) { 15 this.code = code; 16 this.msg = msg; 17 } 18 }
11、新增一个Controller
1 @Slf4j 2 @RestController 3 public class PaymentController { 4 5 6 @Autowired 7 private PaymentService paymentService; 8 9 @Value("${server.port}") 10 private String serverPort; 11 12 @PostMapping("/payment/insert") 13 public CommonResult insert(@RequestBody Payment payment) { 14 int result = paymentService.insert(payment); 15 log.info("====== 插入结果:" + result); 16 if(result > 0) { 17 return new CommonResult(200, "插入数据成功,服务端口:" + serverPort); 18 }else { 19 return new CommonResult(500, "插入数据失败"); 20 } 21 22 } 23 24 @GetMapping("/payment/get/{id}") 25 public CommonResult getPaymentById(@PathVariable("id") Long id) { 26 Payment result = paymentService.getPaymentById(id); 27 28 log.info("====== 查询结果:" + result); 29 if(result != null) { 30 return new CommonResult(200, "查询成功,服务端口:" + serverPort, result); 31 }else { 32 return new CommonResult(500, "查询失败"); 33 } 34 35 } 36 }
12、启动payment模块,查看Eureka注册中心
访问地址:http://localhost:8001/payment/get/1
搭建订单模块、服务消费
1、在父项目上,new module 新建一个模块订单模块,即Eureka客户端 test-springcloud-order8000
2、编写order模块的pom.xml文件,引入Eureka 客户端依赖
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>test-springcloud</artifactId> 7 <groupId>com.test</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>test-springcloud-order8000</artifactId> 13 14 15 <dependencies> 16 17 <!-- eureka client --> 18 <dependency> 19 <groupId>org.springframework.cloud</groupId> 20 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 21 </dependency> 22 23 <!-- spring boot --> 24 <dependency> 25 <groupId>org.springframework.boot</groupId> 26 <artifactId>spring-boot-starter-web</artifactId> 27 </dependency> 28 <dependency> 29 <groupId>org.springframework.boot</groupId> 30 <artifactId>spring-boot-starter-actuator</artifactId> 31 </dependency> 32 <dependency> 33 <groupId>org.springframework.boot</groupId> 34 <artifactId>spring-boot-devtools</artifactId> 35 <scope>runtime</scope> 36 <optional>true</optional> 37 </dependency> 38 39 <dependency> 40 <groupId>org.projectlombok</groupId> 41 <artifactId>lombok</artifactId> 42 <optional>true</optional> 43 </dependency> 44 <dependency> 45 <groupId>org.springframework.boot</groupId> 46 <artifactId>spring-boot-starter-test</artifactId> 47 <scope>test</scope> 48 </dependency> 49 50 </dependencies> 51 52 <build> 53 <finalName>test-springcloud-order8000</finalName> 54 </build> 55 56 </project>
3、编写application.yml配置文件
1 # 端口 2 server: 3 port: 8000 4 5 spring: 6 application: 7 name: cloud-order 8 9 eureka: 10 client: 11 # 表示将自己注册进Eureka Server默认为true 12 register-with-eureka: true 13 # 是否从Eureka Server抓去已有的注册信息,默认是true 14 fetch-registry: true 15 # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址 16 service-url: 17 defaultZone: http://localhost:8761/eureka
4、编写启动类
1 @EnableEurekaClient 2 @SpringBootApplication 3 public class OrderMain80 { 4 public static void main(String[] args) { 5 SpringApplication.run(OrderMain80.class, args); 6 } 7 }
5、编写一个服务调用配置类,注入RestTemplate,调用服务
1 @Configuration 2 public class AppConfig { 3 4 /** 5 * 注入restTemplate,请用请求rest接口 6 * @return 7 */ 8 @Bean 9 // 标注此注解后,RestTemplate就具有了客户端负载均衡能力 10 // 负载均衡技术依赖于的是Ribbon组件~ 11 // RestTemplate都塞入一个loadBalancerInterceptor 让其具备有负载均衡的能力 12 @LoadBalanced 13 public RestTemplate restTemplate(){ 14 return new RestTemplate(); 15 } 16 }
6、编写实体类Payment.java
1 @Data 2 @AllArgsConstructor 3 @NoArgsConstructor 4 public class Payment { 5 private Long id; 6 private String serial; 7 }
7、编写通用返回类
1 @Data 2 @AllArgsConstructor 3 @NoArgsConstructor 4 public class CommonResult<T> { 5 6 private int code; 7 private String msg; 8 private T data; 9 10 public CommonResult(int code, String msg) { 11 this.code = code; 12 this.msg = msg; 13 } 14 }
8、编写Controller,使用RestTemplate调用服务
1 @RestController 2 @Slf4j 3 public class OrderController { 4 5 // public static final String PAYMENT_URL = "http://localhost:8001"; 6 public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE"; 7 8 9 @Autowired 10 private RestTemplate restTemplate; 11 12 @PostMapping("/consumer/payment/insert") 13 public CommonResult<Payment> insert(Payment payment){ 14 log.info("====== 请求插入:" + payment); 15 return restTemplate.postForObject(PAYMENT_URL + "/payment/insert", payment, CommonResult.class); 16 } 17 18 @GetMapping("/consumer/payment/get/{id}") 19 public CommonResult<Payment> getPayment(@PathVariable("id") Long id){ 20 return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class); 21 } 22 }
9、启动order模块,查看Eureka注册中心
访问地址:http://localhost:8000/consumer/payment/get/1