背景
今天开始着手学习SpringCloud的相关内容。
什么是SpringCloud?
SpringCloud是分布式微服务架构的一站式解决方案,它提供了一套简单易用的编程模型,使我们能在SpringBoot的基础上轻松地实现微服务系统的构建。
SpringCloud被称为构建分布式微服务系统的“全家桶”,它并不是某一门技术,而是一系列微服务解决方案或框架的有序集合。它将市面上成熟的、经过验证的微服务框架整合起来,并通过SpringBoot的思想进行再封装,屏蔽掉其中复杂的配置和实现原理,最终为开发人员提供了一套简单易懂、易部署和易维护的分布式系统开发工具包。
SpringCloud中包含了spring-cloud-config、spring-cloud-bus等近20个子项目,提供了服务治理、服务网关、智能路由、负载均衡、断路器、监控跟踪、分布式消息队列、配置管理等领域的解决方案。
实现
微服务架构的四个核心问题:
- 服务很多,客户端该如何访问?
API网关
- 服务很多,服务之间该如何通信?
HTTP,或者RPC
- 如何治理服务?
注册与发现
- 服务挂了怎么办?
熔断机制
解决方案:
- SpringCloud NetFlix,主要由 Eureka、Ribbon、Feign、Hystrix 等组件组成。
- Apache Dubbo + Zookeeper
- SpringCloud Alibaba,主要由 Nacos、Sentinel、Seata 等组件组成。
创建总项目
版本选择
Spring版本依赖。
注:
- Spring官方为我们做好了各种版本选择,在建立项目时需要注意按照要求来选择对应的版本,否则会有各种版本冲突。
创建Maven项目
新建Maven项目(可删除src
目录):
总项目pom文件
编辑总项目的pom.xml
文件,指定各依赖版本,尤其注意SpringCloud
与SpringBoot
的版本兼容问题:
<?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.langjialing</groupId>
<artifactId>springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
<junit.verison>4.12</junit.verison>
<lombok.version>1.16.18</lombok.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<!-- 指定打包方式:pom -->
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<!-- SpringCloud的依赖! -->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringBoot的依赖! 此处需要注意SpringCloud与SpringBoot的版本依赖的冲突!-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 连接数据库的依赖!-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- SpringBoot的启动器!-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.verison}</version>
<scope>test</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- Log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
新建数据库
新建一个MySQL数据库
新建数据表
新建数据表在IDEA中操作:
-
在IDEA中连接刚才创建的数据库:
-
创建一个数据表:
create table dept
(
deptno bigint auto_increment,
dname varchar(60) null,
db_source varchar(60) null,
constraint dept_pk
primary key (deptno)
)
comment '部门表';
- 在数据表中插入数据:
创建模块一:springcloud-api
创建Maven模块
在总项目的目录下新建Module:
创建实体类
package com.langjialing.springcloud.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true) //链式写法
public class Dept implements Serializable { //实例类务必实现序列化!
private Long deptno; //主键
private String dname;
private String db_source; //数据存在哪个数据库
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public Long getDeptno() {
return deptno;
}
public void setDeptno(Long deptno) {
this.deptno = deptno;
}
public String getDb_source() {
return db_source;
}
public void setDb_source(String db_source) {
this.db_source = db_source;
}
}
至此,springcloud-api
模块结束。
模块的文件路径
创建模块二:服务提供者
创建Maven模块
在总项目的目录下新建Module:
编辑pom.xml
此模块pom.xml
文件的关键在于需要引入刚才创建的springcloud-api
。
<?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>springcloud</artifactId>
<groupId>com.langjialing</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-provider-dept-8001</artifactId>
<properties>
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
</properties>
<dependencies>
<!-- 需要拿到实体类,因此需要配置api Module。这个类是我们刚才自己写的Module! -->
<dependency>
<groupId>com.langjialing</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
</dependencies>
</project>
编辑application.yaml
在resources
目录下创建application.yaml
文件:
server:
port: 8001
mybatis:
type-aliases-package: com.langjialing.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
spring:
application:
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
username: root
password:
编辑mybatis-config.xml
在resources
目录下创建mapper
路径,并在路径中创建mybatis-config.xml
文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 根标签 -->
<configuration>
<!-- 开启二级缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
创建DeptDao
package com.langjialing.springcloud.dao;
import com.langjialing.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DeptDao {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
创建DeptMapper.xml
在resources
目录下mybatis
路径中,继续创建mapper
路径,在mapper
路径下创建mybatis-config.xml
文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 根标签 -->
<mapper namespace="com.langjialing.springcloud.dao.DeptDao">
<insert id="addDept" parameterType="Dept">
insert into dept(dname, db_source)
values (#{dname},DATABASE());
</insert>
<select id="queryById" resultType="Dept" parameterType="Long">
select * from dept where deptno = #{deptno};
</select>
<select id="queryAll" resultType="Dept">
select * from dept;
</select>
</mapper>
创建service层
创建DeptService接口
package com.langjialing.springcloud.service;
import com.langjialing.springcloud.pojo.Dept;
import java.util.List;
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
创建DeptService实现类
package com.langjialing.springcloud.service;
import com.langjialing.springcloud.dao.DeptDao;
import com.langjialing.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
private DeptDao deptDao;
@Override
public boolean addDept(Dept dept) {
return deptDao.addDept(dept);
}
@Override
public Dept queryById(Long id) {
return deptDao.queryById(id);
}
@Override
public List<Dept> queryAll() {
return deptDao.queryAll();
}
}
创建controller层
package com.langjialing.springcloud.controller;
import com.langjialing.springcloud.pojo.Dept;
import com.langjialing.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//提供Restful服务
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return deptService.queryById(id);
}
@GetMapping("/dept/list")
public List<Dept> queryAll(){
return deptService.queryAll();
}
}
创建启动类
package com.langjialing.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args){
SpringApplication.run(DeptProvider_8001.class, args);
}
}
测试模块
模块的文件路径
创建模块三:服务消费者
创建Maven模块
编辑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>springcloud</artifactId>
<groupId>com.langjialing</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-dept-80</artifactId>
<properties>
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.langjialing</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
编辑application.yaml
这里仅需要配置一个端口号即可。
server:
port: 80
注册Bean - RestTemplate
RestTemplate是什么?
package com.langjialing.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
创建消费者的controller
package com.langjialing.springcloud.controller;
import com.langjialing.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class DeptConsumerController {
@Autowired
private RestTemplate restTemplate; // 提供多种便捷访问远程http服务的方法,是一个简单的restful服务模板
//由于请求地址的前半部分是一致的,所以这里可将前缀提取出来
private static final String REST_URL_PREFIX = "http://localhost:8001/";
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id, Dept.class);
}
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add", dept, boolean.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
}
}
创建启动类
package com.langjialing.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
服务消费者调用服务提供者测试
这里注意我们的请求地址:localhost/consumer/dept.list
、localhost/consumer/dept/get/1
,localhost/consumer/dept/add?danme='游戏部'
。
这与此前单体服务的请求产生了本质区别,是微服务的基本实现。
模块的文件路径
总结
至此,我们已经完成了基本的微服务环境搭建。
后续我们将连载springcloud生态的各个组件,最终实现springcloud的完整生态。