SpringCloud(一)基础入门
1. 什么是SpringCloud
1.1 SpringBoot介绍
基于springboot提供的一套微服务解决方案,包裹服务注册于发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
利用springboot的开发便利性,巧妙的简化了分布式系统基础设施的开发,springcloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等,他们都可以用springboot的开发风格做到一键启动和部署。
springcloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。
1.2. SpringCloud和SpringBoot的关系
- SpringBoot专注于快速方便的开发单个个体微服务。 -jar
- SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、微代理、事务总线、全局锁、决策竞争、分布式会话等等集成服务。
- SpringBoot可以离开SpringCloud独立使用,开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系。
1.3. Dubbo和SpringCloud技术选型
1.3.1 分布式 + 服务治理Dubbo
目前成熟的互联网架构:应用服务化拆分 + 消息中间件
1.3.2 Dubbo 和 SpringCloud对比
功能 | Dubbo | Spring |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netflix Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netflix Hystrix |
服务网关 | 无 | Spring Cloud Netflix Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总线 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
最大区别:SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式。
严格来说,这两种各有优势,虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免不了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更加合适。
品牌机和主装机的区别
很明显,SpringCloud的功能比Dubbo更强大,涵盖面更广,而且作为Spring的明星项目,它也能够与SpringFramework、SpringBoot、SpringData、SpringBatch等其他Spring项目完美融合,这些对于微服务而言是至关重要的,使用Dubbo构建的微服务架构就像组装电脑,各环节的选择自由度很高,但是最终结果很有可能因为一条内存质量不行就点不亮了,总是让人不怎么放心,但是如果你是一个高手,那么这些都不是问题;而SpringCLoud就像品牌机,在SpringSource的整合下,做了大量的兼容性测试,保证了机器拥有更高的稳定性,但是如果要使用非原装组件外的东西,就需要对其基础有足够的了解。
社区支持与更新力度
最为重要的是,Dubbo停止了5年左右的更新,虽然2017年7月项目重启了,对于技术发展的新需求,需要有开发者自行拓展升级(比如当当网弄出DubboX),这对于很多想要采用微服务架构的中小软件组织,显然是不太合适的,中小公司没有这么强大的技术能力去修改Dubbo源码+周边的一整套解决方案,并不是每个公司都有阿里的大牛+真实的线上生成环境测试过。
总结
曾风靡国内开源RPC服务框架Dubbo在重启维护后,令许多用户为之雀跃,但同时,也迎来了一些质疑的声音,互联网技术发展迅速,Dubbo是否能够跟上时代?Dubbo与SpringCloud相比又有何优势和劣势?是否有相关举措保证Dubbo的后续更新频率?这些都是目前未知的。
2. Rest学习环境搭建:服务提供者
- 新建Maven项目:springcloud,切记Packageing是pom模式
- 主要是定义POM文件,将后续各个子模块公用的jar包等统一提取出来,类似一个抽象父类
- 父级pom.xml
<!--打包方式pom-->
<packaging>pom</packaging>
<!--版本号-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<lombok.version>1.18.12</lombok.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<dependencyManagement>
<dependencies>
<!--SpringCloud依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SpringBoot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<!--数据连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--springboot启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</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>
<!--日志门面-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
- 新建一个module:springcloud-api
- 配置pom.xml
<!--当前的Module自己需要的依赖,如果父依赖中已经配置了版本,就不用写了-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
- 创建数据库:db01
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`d_no` bigint(20) NOT NULL AUTO_INCREMENT,
`d_name` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`db_source` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`d_no`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '部门表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES (1, '开发部', 'blog_test');
INSERT INTO `dept` VALUES (2, '人事部', 'blog_test');
INSERT INTO `dept` VALUES (3, '财务部', 'blog_test');
INSERT INTO `dept` VALUES (4, '市场部', 'blog_test');
INSERT INTO `dept` VALUES (5, '运维部', 'blog_test');
SET FOREIGN_KEY_CHECKS = 1;
- 创建pojo文件,添加Dept.java
@Data
@NoArgsConstructor
@Accessors(chain = true) // 链式写法
public class Dept implements Serializable {
// Dept 实体类, orm 类表关系映射
/**
* 主键
*/
private Long deptNo;
/**
* 部门名
*/
private String deptName;
/**
* 数据来源
* 同一个信息可能存在不同的数据库
*/
private String dbSource;
public Dept(String deptName) {
this.deptName = deptName;
}
/*
链式写法样例,持续向下赋值
Dept dept = new Dept();
dept.setDNo(11).setDName('ssss');
*/
}
- 新建module:springcloud-provider-dept-8001
- pom.xml配置
<dependencies>
<!--需要拿到实体类,要配置api-module-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<!--springboot启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<!--web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jetty,等价tomcat-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- 配置application.yml
server:
port: 8001
# mybatis配置
mybatis:
type-aliases-package: com.example.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
# spring配置
spring:
application:
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
- 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" /> <!-- 全局映射器启用缓存 -->
<setting name="useGeneratedKeys" value="true" /> <!-- 允许 JDBC 支持自动生成主键 -->
<setting name="defaultExecutorType" value="REUSE" /> <!-- 配置默认的执行器 -->
<setting name="logImpl" value="SLF4J" /> <!-- 指定 MyBatis 所用日志的具体实现 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> 驼峰式命名 -->
</settings>
</configuration>
- 创建DeptMapper.java
@Mapper
public interface DeptMapper {
/**
* 添加一个部门
* @param dept 部门实体
* @return 添加结果
*/
boolean addDepartment(Dept dept);
/**
* 通过id查部门
* @param id 部门id
* @return 部门信息
*/
Dept queryDeptById(Long id);
/**
* 查询所有部门
* @return 部门列表
*/
List<Dept> queryAllDept();
}
- 创建DeptMapper.xml映射文件
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.springcloud.mapper.DeptMapper">
<resultMap id="deptResult" type="com.example.springcloud.pojo.Dept">
<result column="d_no" property="deptNo"/>
<result column="d_name" property="deptName"/>
<result column="db_source" property="dbSource"/>
</resultMap>
<sql id="selectDept">
select
d_no,
d_name,
db_source
from dept
</sql>
<insert id="addDepartment" parameterType="com.example.springcloud.pojo.Dept">
insert into db01.dept (d_name, db_source)
value (#{dName}, DATABASE())
</insert>
<select id="queryDeptById" parameterType="Long" resultMap="deptResult">
<include refid="selectDept"/>
where d_no = #{id}
</select>
<select id="queryAllDept" resultMap="deptResult">
<include refid="selectDept"/>
</select>
</mapper>
- 创建DeptService.java
public interface DeptService {
/**
* 添加一个部门
* @param dept 部门实体
* @return 添加结果
*/
boolean addDepartment(Dept dept);
/**
* 通过id查部门
* @param id 部门id
* @return 部门信息
*/
Dept queryDeptById(Long id);
/**
* 查询所有部门
* @return 部门列表
*/
List<Dept> queryAllDept();
}
- 创建DeptServiceImpl.java
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
private DeptMapper deptMapper;
/**
* 添加一个部门
*
* @param dept 部门实体
* @return 添加结果
*/
@Override
public boolean addDepartment(Dept dept) {
return deptMapper.addDepartment(dept);
}
/**
* 通过id查部门
*
* @param id 部门id
* @return 部门信息
*/
@Override
public Dept queryDeptById(Long id) {
return deptMapper.queryDeptById(id);
}
/**
* 查询所有部门
*
* @return 部门列表
*/
@Override
public List<Dept> queryAllDept() {
return deptMapper.queryAllDept();
}
}
- 创建DeptController.java
@RestController
@RequestMapping("/dept")
public class DeptController {
/**
* 注入服务
*/
@Autowired
private DeptService deptService;
/**
* 添加部门
* @param dept 部门对象
* @return 添加结果
*/
@PostMapping("/add")
public Boolean addDept(@RequestBody Dept dept) {
return deptService.addDepartment(dept);
}
/**
* 通过部门id查询部门
* @param id 部门id
* @return 部门信息
*/
@GetMapping("/get/{id}")
public Dept getDept(@PathVariable("id") Long id) {
return deptService.queryDeptById(id);
}
/**
* 查询所有部门
* @return 部门列表
*/
@GetMapping("/list")
public List<Dept> queryAll() {
return deptService.queryAllDept();
}
}
- 创建SpringBoot启动类:DeptProvider
@SpringBootApplication
public class DeptProvider {
public static void main(String[] args) {
SpringApplication.run(DeptProvider.class,args);
}
}
- 启动项目
- 访问地址
- GET:http://localhost:8001/dept/get/1
- GET:http://localhost:8001/dept/list
- POST:http://localhost:8001/dept/add
- 参数:deptName
总结:服务提供者和正常的SpringBoot项目一样
3.Rest学习环境搭建:服务消费者
- 创建新module:springcloud-consumer-dept-80
- 配置pom.xml依赖
<!--实体类+web-->
<dependencies>
<!--实体类-->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- 配置application.yml
server:
port: 80
- 创建ConfigBean
@Configuration
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
- 创建DeptConsumerController
@RestController
@RequestMapping("/consumer/dept")
public class DeptConsumerController {
/**
* 理解:消费者不应该有service层
* RestTemplate 供我们直接调用就可以了!注册到Spring中
* (url, 实体:Map, Class<T> responseType)
* 什么方式拿,去哪拿,拿什么类型东西
* 提供多种便捷访问远程http服务的方法
* 简单的restful服务模板
*/
@Autowired
private RestTemplate restTemplate;
private static final String REST_URL_PREFIX = "http://localhost:8001/dept";
/**
* 客户端添加一个部门
* @param dept 部门对象
* @return 添加结果
*/
@RequestMapping("/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/add", dept, Boolean.class);
}
/**
* 通过部门id获取部门信息
* @param id 部门id
* @return 部门信息
*/
@RequestMapping("get/{id}")
public Dept get(@PathVariable("id") Long id) {
return restTemplate.getForObject(REST_URL_PREFIX + "/get/" + id, Dept.class);
}
/**
* 查询所有部门
* @return 部门列表
*/
@RequestMapping("/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/list", List.class);
}
}
-
分别启动服务提供者,消费者
-
访问路径:
总结:相对于消费者来说,他不需要进行业务处理,只需要知道服务接口是怎样的,通过RestTemplate 进行接口调用就好,需要注册到Spring中
(url, 实体:Map, Class
responseType) 通过什么方式获取,去哪获取,获取什么类型
提供多种便捷访问远程http服务的方法
简单的restful服务模板