Spring Cloud 00 微服务的概念
1 微服务概述
1.1 什么是微服务
- 微服务是近几年流行的一种架构思想,最早由Martin Fowler提出
- 通常而言
- 微服务架构是一种架构模式,或者说是一种架构风格,
- 它提倡将单一的应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程内,服务之间互相协调,互相配置,为用户提供最终价值。
- 服务之间采用轻量级的通信机制互相沟通,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,
- 另外,应尽量避免统一的,集中式的服务管理机制,
- 对具体的一个服务而言,应根据业务上下文,选择合适的语言,工具对其进行构建,
- 可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储;
- 说人话
- 微服务就是把以前的 all in one的应用进行拆分,使得其中的各个部分可以独立运行。在配上合理的管理机制,使得这些组件可以灵活的根据实际需求来进行部署
1.2 微服务优缺点
- 优点
- 每个服务足够内聚,足够小,代码容易理解,这样能聚焦一个指定的业务功能或业务需求;
- 开发简单,开发效率提高,一个服务可能就是专一的只干一件事;
- 微服务能够被小团队单独开发,这个小团队是2~5人的开发人员组成;
- 微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的。
- 微服务能使用不同的语言开发。
- 易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如jenkins,Hudson,bamboo
- 微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果。无需通过合作才能体现价值。
- 微服务允许你利用融合最新技术。
- 微服务只是业务逻辑的代码,代码功能明确
- 每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一数据库
- 缺点
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度,随着服务的增加,运维的压力也在增大
- 系统部署依赖
- 服务间通信成本
- 数据一致性
- 系统集成测试
- 性能监控.....
1.3 微服务涉及到的技术组件
技术条目 | 解决方案 |
---|---|
微服务开发 | Spring、SpringBoot |
服务配置与管理 | Archaius、Diamond |
服务注册与发现 | Eureka、Zookeeper、Consul |
服务间调用 | rest、RPC、gRPC |
熔断机制 | Hystrix、Envoy |
负载均衡 | Ribbon、Nginx |
用户调用服务 | Feign、OpenFeign |
消息队列 | Kafka、RocketMQ、ActiveMQ |
服务配置中心 | SpringCloudConfig、Chef |
API网关 | Zuul |
服务监控 | Zabbix、Nagios、Metrics、Specatator |
全链路追踪 | Zipkin、Brave、Dapper |
服务部署 | Docker |
数据流 | SpringCloud Stream(封装与Redis,Rabbit,Kafka等发 送接收消息) |
消息事件总线 | SpringCloud Bus |
1.4 微服务的核心问题
-
这么多的服务,用户该怎么访问?
- API网关、负载均衡
-
这么多的服务,服务之间如何通信?
- 服务调用
-
这么多的服务,我该怎么治理?
- 服务注册与发现
-
这么多的服务,其中一个挂了怎么办?
- 熔断降级机制
2 SpringCloud入门
2.1 是什么
- 官网
- SpringCloud, 基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
- SpringCloud利用SpringBoot的开发便利性,巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等,他们都可以用SpringBoot的开发风格做到一键启动和部署。
- SpringBoot并没有重复造轮子,它只是将目前各家公司开发的比较成熟,经得起实际考研的服务框架组合起来,通过SpringBoot风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂,易部署和易维护的分布式系统开发工具包
- SpringCloud 是 分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶
- 说人话
- SpringCloud就是一个工具箱,要什么从里面拿就好了
2.2 SpringCloud和SpringBoot
- SpringBoot专注于快速方便的开发单个个体微服务。
- SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等集成服务。
- SpringBoot可以离开SpringClooud独立使用,开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系
- SpringBoot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架
2.3 微服务常用套件
-
Spring Cloud Netflix
服务 组件 分布式配置 Archaius 服务注册与发现 Eureka 服务熔断 Hystrix 服务调用 Feign,http API网关 Zuul 负载均衡 Ribbon -
Apache Dubbo + Zookeeper
服务 组件 API网关 无,自己搭配 服务通信 Dubbo,RPC 服务注册中心 Zookeeper 熔断与降级 无,自己搭配 -
Spring Cloud Alibaba
服务 组件 分布式配置 Nacos 服务注册与发现 Nacos 服务熔断 Sentinel 服务调用 Dubbo RPC API网关 Dubbo Proxy 负载均衡 Dubbo LB
2.4 构建基础框架
1)父工程
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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.pbx</groupId> <artifactId>SpringCloud</artifactId> <version>1.0.0</version> <modules> <module>API</module> <module>provider-default-8001</module> </modules> <properties> <project.bulid.sourceEncoding>UTF-8</project.bulid.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <!--依赖版本管理--> <spring.cloud.version>Hoxton.SR10</spring.cloud.version> <spring.boot.version>2.3.8.RELEASE</spring.boot.version> <mysql.version>8.0.22</mysql.version> <mybatis.version>2.1.4</mybatis.version> <lombok.version>1.18.16</lombok.version> <druid.version>1.2.4</druid.version> <hutool.version>5.5.8</hutool.version> <swagger.version>3.0.0</swagger.version> </properties> <!--设置打包方式--> <packaging>pom</packaging> <dependencyManagement> <dependencies> <!--Spring Cloud 依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--Spring Boot 依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--Database--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <!--devtools--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>${spring.boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>${spring.boot.version}</version> </dependency> </dependencies> </dependencyManagement> </project>
#### 2)API组件
- 建表SQL
~~~sql
CREATE TABLE `department` (
`d_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '部门id',
`d_name` varchar(255) DEFAULT NULL COMMENT '部门名',
PRIMARY KEY (`d_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `department` VALUES (1, '开发部');
INSERT INTO `department` VALUES (2, '策划部');
INSERT INTO `department` VALUES (3, '市场部');
INSERT INTO `department` VALUES (4, '销售部');
INSERT INTO `department` VALUES (5, '人事部');
-
maven依赖
<dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> </dependency> </dependencies>
-
Message
@Data @NoArgsConstructor @AllArgsConstructor public class Message implements Serializable { private int code; private String msg; private Object data; public Message(int code, String msg) { this(code, msg, null); } }
-
Department
@Data @AllArgsConstructor @NoArgsConstructor public class Department implements Serializable { private Long id; private String name; }
-
Swagger配置类
@EnableOpenApi @Configuration public abstract class Swagger3 { @Bean public Docket docket() { return new Docket(DocumentationType.OAS_30).apiInfo(apiInfo()). select().apis(RequestHandlerSelectors.basePackage("com.pbx.springcloud.controller")).build(); } protected abstract ApiInfo apiInfo(); }
3)provider-default-8001
-
maven依赖
<dependencies> <!--api--> <dependency> <groupId>springcloud</groupId> <artifactId>API</artifactId> <version>1.0.0</version> </dependency> <!--database--> <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> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--devtools--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> </dependencies>
-
application.yaml
server: port: 8001 spring: application: name: provider-default-8001 datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/cloud-db01?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.pbx.springcloud.pojo
-
mapper
-
接口
@Mapper @Repository public interface DepartmentMapper { boolean insertDepartment(Department department); Department getDepartmentById(@Param("id") Long id); List<Department> getDepartmentList(); Department getByName(@Param("name") String name); }
-
xml文件
<mapper namespace="com.pbx.springcloud.mapper.DepartmentMapper"> <resultMap id="BaseMap" type="department"> <id property="id" column="d_id" jdbcType="BIGINT"/> <id property="name" column="d_name" jdbcType="VARCHAR"/> </resultMap> <insert id="insertDepartment" parameterType="department"> insert into department (d_name) values (#{name}) </insert> <select id="getDepartmentById" parameterType="long" resultMap="BaseMap"> select * from department where d_id = #{id} </select> <select id="getDepartmentList" resultType="list" resultMap="BaseMap"> select * from department </select> <select id="getByName" resultMap="BaseMap"> select * from department where d_name = #{name} </select> </mapper>
-
-
service
-
接口
public interface DepartmentService { Department insertDepartment(String name); Department getDepartmentById(@Param("id") Long id); List<Department> getDepartmentList(); }
-
实现类
@Service public class DepartmentServiceImpl implements DepartmentService { @Autowired private DepartmentMapper mapper; @Override public Department insertDepartment(String name) { mapper.insertDepartment(new Department(name)); return mapper.getByName(name); } @Override public Department getDepartmentById(Long id) { return mapper.getDepartmentById(id); } @Override public List<Department> getDepartmentList() { return mapper.getDepartmentList(); } }
-
-
controller
@RestController @RequestMapping("/provider") @Slf4j public class DepartmentController { @Autowired private DepartmentService service; @PostMapping("/insert") public Message insert(@RequestBody String name, HttpServletRequest request) { log.info("host:" + request.getRemoteAddr() + " try to insert a department, name: " + name); Department department = new Department(); department.setName(name); Department res = service.insertDepartment(name); if (res != null && res.getId() != null) { log.info("insert successes, new department = " + res); return new Message(200, "插入成功", res); } else { log.info("insert failed"); return new Message(400, "插入失败", res); } } @GetMapping("/get/{id}") public Message getDepartmentById(@PathVariable("id") Long id, HttpServletRequest request) { log.info("host:" + request.getRemoteAddr() + " try to get a department by id, id = " + id); Department res = service.getDepartmentById(id); if (res != null) { log.info("search successes, department = " + res); return new Message(200, "查找成功", res); } else { log.info("search failed"); return new Message(400, "查找失败"); } } @GetMapping("/get/all") public Message getAllDepartment(HttpServletRequest request) { log.info(request.getRemoteAddr() + " try to get department list"); List<Department> list = service.getDepartmentList(); if (list.size() > 0) { log.info("search success, + department nums = " + list.size()); return new Message(200, "查找成功", list); } else { log.info("search failed"); return new Message(400, "查找失败"); } } }
-
集成Swagger
-
配置类
@EnableOpenApi @Configuration public class SwaggerConfig extends Swagger3 { @Override public ApiInfo apiInfo() { return new ApiInfoBuilder().title("provider-default-8001").description("default cloud microservice provider-8001").version("1.0").build(); } }
-
4)consumer-default-80
-
maven依赖
<dependencies> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> </dependency> <!--api--> <dependency> <groupId>springcloud</groupId> <artifactId>API</artifactId> <version>1.0.0</version> </dependency> <!--devtools--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
-
application.yaml
server: port: 80 spring: application: name: consumer-default-80
-
RestTemplate
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
-
controller
@RestController @RequestMapping("/consumer") public class ConsumerController { @Autowired private RestTemplate template; private static final String PROVIDER_URL_PREFIX = "http://127.0.0.1:8001/provider"; @GetMapping("/add") public Message add(Department department) { return template.postForObject(PROVIDER_URL_PREFIX + "/insert", department, Message.class); } @GetMapping("/get/{id}") public Message getDepartmentById(@PathVariable Long id) { return template.getForObject(PROVIDER_URL_PREFIX + "/get/" + id, Message.class); } @GetMapping("/get/all") public Message getDepartmentList() { return template.getForObject(PROVIDER_URL_PREFIX + "/get/all", Message.class); } }
-
配置Swagger
@Configuration @EnableOpenApi public class SwaggerConfig extends Swagger3 { @Override protected ApiInfo apiInfo() { return new ApiInfoBuilder() .title("consumer-default-80") .description("Default Spring Cloud Micro Service Consumer :80") .version("Default") .build(); } }