SpringCloud入门
前言
目前Springboot是新一代的JavaEE开发标准,目前很多企业都在送Spring转型向SpringBoot进行开发,这样带来了一个问题,随着体系越来越大,用户越来越多,一个SpringBoot项目不能承载所有的服务,因此需要多个SpringBoot项目来分别负责多个业务,简单来说,就是模块化
微服务的4个核心问题
- 服务很多,客户端该怎么访问
- 服务很多,服务之间如何通信
- 服务如何治理
- 服务宕机了怎么办
解决方案:
SpringCloud
SpringCloud是一个生态!
- Spring Cloud NetFlix(停止维护):一站式解决方案
访问:api网关,zuul组件
通信:Feign,基于Http通信方式,同步,阻塞
服务注册发现:Eureka
熔断机制:Hystrix,使得单个服务出现故障时,整个集群可用性不受到影响 - Apache Dubbo Zookeeper:半自动,需要整合别人的东西
访问:没有,使用第三方组件,或者自己实现
通信:Dubbo,高性能
服务注册发现:Zookeeper
熔断机制:借助于Hystrix
Dubbo这个方案并不完善
- Spring Cloud Alibaba:最新的一站式解决方案
更加简单
新概念:服务网格(Server Mesh)——下一代的微服务标准
什么是微服务
微服务强调的是服务的大小,这个服务该去处理什么。是具体解决某一个问题的服务应用
什么是微服务架构
一种新的架构形式,微服务≠微服务架构,别弄混了,这种架构是由Martin Fowler在2014年提出的。
微服务架构是一种架构模式,它提倡将单一的应用程序划分成一组小的服务,服务之间相互协调、互相配置,每个服务都有具体的业务,独立部署到生产环境中,可以不同语言,也可以不同数据存储。
简单来说:解耦
微服务优缺点
优点
- 每个服务足够内聚、足够小,代码容易理解,这样才能聚集一个指定的业务或业务需求
- 开发简单,开发效率高,一个服务只干一件事情
- 可以独立开发
- 松耦合,在开发或者部署都是独立的
- 可以使用不同语言进行开发
- 易于于第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如Jenkins,Hudson,bamboo
- 微服务易于维护
- 允许融合最新技术
- 微服务只是业务逻辑的代码,不会和HTML、CSS或其他界面混合
10.每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一的数据库
缺点
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度随着服务的增加,压力也在增大
- 系统部署依赖
- 服务间通信成本增加
- 数据一致性可能有问题
- 系统集成测试
- 性能监控
微服务技术栈
为什么使用SpringCloud作为微服务架构
1、选型依据
- 整体解决方案和框架成熟度
- 社区热度
- 可维护性
- 学习曲线
2、IT公司使用的微服务架构有哪些
- 阿里:Dubbo+HFS
- 京东:JSF
- 新浪:Motan
- 当当网:DubboX
什么是SpringCloud
SpringCloud,基于SpringBoot提供的一套微服务解决方案,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等,最终给开发者留出一套简单易懂,易于部署和易于维护的分布式系统开发工具包。
它是将服务框架组合起来,通过SpringBoot风格进行再次封装,屏蔽了复杂的配置和实现原理。
SpringCloud和SpringBoot关系
- SpringBoot用于构建微服务,而SpringCloud用于协调微服务
- SpringBoot专注于快速方便的开发单个个体微服务
- SpringCloud将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等集成服务
- SpringBoot可以离开SpringCloud独立使用,但是SpringCloud只能依赖于SpringBoot
- SpringBoot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架
SpringCloud版本选择
一般选择最新的稳定版即可。
创建SpringCloud项目
建立一个Maven项目(父工程)
我们在IDEA中建立一个普通的Maven项目,并把它作为一个父工程,删除src目录
导入依赖
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<junit.version>4.12</junit.version>
<lombok.version>1.8.22</lombok.version>
</properties>
<!--打包方式 pom-->
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<!--springCloud的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springboot的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.6.6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</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>2.2.2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</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>1.2.17</version>
</dependency>
</dependencies>
</dependencyManagement>
这里的dependencyManagement
说明父项目只负责依赖的管理,并没有给子项目都导入依赖,而是子项目需要什么依赖自己导入即可。
创建子项目
我们在父项目下:
接口服务
新建一个module——springcloud-api,专注于写接口即可
导入lombok依赖
<!--当前的module需要的依赖,如果父依赖中已经配置了版本,这里就不需要写了-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
实现接口类Dept
package com.cjh.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 Dept(String dname) {
this.dname = dname;
}
/*
* 链式写法
Dept dept = new Dept();
dept.setDeptNo(11).setDname('sss').setDb_source('001');
* */
}
提供服务(服务端)
新建一个module——springcloud-provider-dept-8081,专注于提供服务
导入依赖
<?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.cjh</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-provider-dept-8081</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--我们需要拿到我们的实体类,所以需要配置api module-->
<dependency>
<groupId>com.cjh</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jetty-->
<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>
</project>
这里我们通过依赖,拿到我们的实体类
Deptmapper
package com.cjh.springcloud.mapper;
import com.cjh.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DeptMapper {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
DeptController
package com.cjh.springcloud.controller;
import com.cjh.springcloud.pojo.Dept;
import com.cjh.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();
}
}
DeptService
package com.cjh.springcloud.service;
import com.cjh.springcloud.pojo.Dept;
import java.util.List;
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
DeptServiceImpl
package com.cjh.springcloud.service;
import com.cjh.springcloud.mapper.DeptMapper;
import com.cjh.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 DeptMapper deptMapper;
@Override
public boolean addDept(Dept dept) {
return deptMapper.addDept(dept);
}
@Override
public Dept queryById(Long id) {
return deptMapper.queryById(id);
}
@Override
public List<Dept> queryAll() {
return deptMapper.queryAll();
}
}
DeptMapper.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.cjh.springcloud.mapper.DeptMapper">
<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>
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核心配置文件-->
<configuration>
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>
application.yml
server:
port: 8081
#mybatis配置
mybatis:
type-aliases-package: com.cjh.springcloud.pojo
mapper-location: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/mybatis-config.xml
#spring配置
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&useSSL=false&serverTimezone = GMT
username: root
password: 123456
使用服务(用户)
我们建立一个消费者,来使用我们的服务
ConfigBean
package com.cjh.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();
}
}
我们需要建立一个配置类,来使用我们的RestTemplate
RestTemplate:提供多种便捷访问远程http服务的方法,简单的restful服务模板
DeptConsumerController
package com.cjh.springcloud.controller;
import com.cjh.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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 {
//理解:消费者,不应该有service层
//RestTemplate
//(url,实体:Map,Class<T> responseType)
@Autowired
private RestTemplate restTemplate; //提供多种便捷访问远程http服务的方法,简单的restful服务模板
private static final String REST_URL_PREFIX= "http://localhost:8081";
@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);
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
}
}
由于消费者是没有service层的概念的,因此我们需要通过RestTemplate
来实现远程访问服务端。
application.yml
server:
port: 80
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.cjh</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-dept-80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!--实体类+web-->
<dependencies>
<dependency>
<groupId>com.cjh</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<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>
</project>
总结
我们通过这一个实例,就可以明显的看出,我们在SpringBoot项目中,所有的服务都在一个项目中完成,我们的员工管理系统,实体类、接口类、接口实现类、配置文件、调用类、视图层等等都在一个项目中,但是我们通过SpringCloud,我们把所有的服务都拆成了一个个模块,单独的一个服务,这样就便于管理和维护。