好记性不如烂笔头。有些事情看着简单,做起来可能就会碰到问题。这是我从视频上看到的一个例子,决定把它写下来
1、先介绍一下单体应用架构和微服务的区别
单个应用包含所有功能,不涉及多个应用的互联互调,便于在团队之间开发与测试。
易于部署:只需将单个应用打成war或jar包,进行部署到Tomcat即可,运维起来比较方便。
易于整体扩展:当应用负载压力大时,将这个应用复制几份,分别部署在不同的服务器上,再通过负载
均衡即可提高应用的并发能力。
缺点
复杂性高:由于是单个应用,所以整个项目文件包含的模块非常多,导致模块的边界模糊、依赖关
系不清晰、代码的质量参差不齐,混乱的堆在一起,使得整个项目非常复杂。以致每次修改代码,都非
常小心,可能添加一个简单的功能,或者修改一个Bug都会带来隐藏的缺陷。
技术债务:随着时间的推
移、需求的变更和技术人员的更替,会逐渐形成应用程序的技术债务,并且越积越多。
碍技术创新:
对于单体应用来说,技术是在开发之前经过慎重评估后选定的,每个团队成员都必须使用相同的开发语
言、持久化存储及消息系统
微服务架构的优缺点
优点
1. 易于开发和维护:一个微服务只会关注一个特定的业务功能,所以业务清晰、代码量较少。开发和
维护单个微服务相对简单。
2. 单个微服务启动较快
3. 局部修改容易部署:单一应用只要有修改,就得重新部署整个应用。微服务解决了这样的问题。一
般来说,对某个微服务进行修改,只需要重新部署这个服务即可。
4. 技术栈不受限制:在微服务架构中,可以结合项目业务及团队的特点,合理的选择技术栈。不同的模块可以用不同的开发语言
5. 按需伸缩:可根据需求,实现细粒度的扩展。
缺点
1. 运维要求高:更多的服务意味着要投入更多的运维。
2. 分布式固有的复杂性:使用微服务构建的是分布式系统。对于一个分布式系统,系统容错、网络延
迟、分布式事务等都会带来巨大的问题。
3. 接口调整成本高:微服务之间通过接口进行通信。如果修改某一个微服务的API,可能所有用到这
个接口的微服务都需要进行调整。
2、什么是 Spring Cloud
Spring Cloud,基于 Spring Boot 提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监
控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型
中立的开源组件。
官方译文:构建分布式系统不用特别的复杂且避免容易出现的错误。Spring Cloud为最常见的分布式系统模式
提供了一个简单和可访问的编程模型,帮助开发人员构建弹性、可靠和协调的应用程序。SpringCloud构建在
SpringBoot之上,使开发人员很容易开始工作并迅速提高生产力。
3、SpringBoot和SpringCloud的关系
Spring Boot 可以离开 Spring Cloud 单独使用开发项目,但是Spring Cloud离不开SpringBoot,属于依赖的
关系.
Spring Boot 专注于快速方便的开发单个个体微服务,Spring Cloud 关注全局的服务治理框架。
Spring Cloud 是关注全局的微服务协调整理治理框架,它将 Spring Boot 开发的一个个单体微服务整合并管
理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策
竞选、分布式会话等等集成服务
4、参考资料
官网: http://spring.io/projects/spring-cloud
各组件说明(中文版):https://springcloud.cc/spring-cloud-netflix.html
详细文档版:
英文版:https://cloud.spring.io/spring-cloud-static/Finchley.SR2/single/spring-cloud.html
中文版:https://springcloud.cc/spring-cloud-dalston.html
Spring Cloud 中国社区:http://springcloud.cn/
Spring Cloud 中文网:https://springcloud.cc/
Spring Cloud 目前集合了21种分布式微服务架构技术
5、开发环境
jdk1.8 (Spring Boot 推荐jdk1.8及以上): java version "1.8.0_151"
Maven 3.x (maven 3.2 以上版本):Apache Maven 3.3.9
IntelliJ IDEA :IntelliJ IDEA 2018.2.1 x64
Spring Boot :Spring Boot 2.0.7
Spring Cloud 使用当前最新稳定版本:Finchley SR2 , 搭配 Spring Boot 2.0.7 稳定版
一:安装maven,在maven的settings.xml 中添加如下依赖。
<!--开始处更改下载依赖的存放路径, 以下目录需要已经创建-->
<localRepository>D:\javasource\maven-repository</localRepository>
<!--在 mirrors 标签下 添加阿里云maven私服库-->
<mirrors>
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
</mirrors>
<!-- 在 profiles 标签下指定jdk版本 -->
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
二:idea 配置maven
settings----搜索maven--找到Maven home directory,配置maven的安装路径
三:以 商品管理模块 做一个微服务架构通用案例,消费者Consumer(Client)通过REST调用 提供者
Provider(Server)提供的商品管理服务。
Maven的分包分模块架构
一个 Project 父工程下有多个 Module 子模块
microservice-cloud-01 父工程(Project)下初始化 3个子模块(Module):
microservice-cloud-02-api 封装的整体Entity/接口/公共配置等
microservice-cloud-03-provider-product-8001 微服务的服务提供者Provider
microservice-cloud-04-consumer-product-80 微服务的服务消费者Consumer
浏览器发送URL请求服务默认的端口号都是80,因此只需输入IP或域名即可,可省略 :80
1)构建一个空的工程。
2)创建父工程(pom类型)
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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.run.springcloud</groupId>
<artifactId>microservice-cloud-01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.7.RELEASE</version>
<relativePath/>
</parent>
<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>
<!-- spring cloud 采用 Finchley.SR2 版本 -->
<spring-cloud.version>Finchley.SR2</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<!--maven不支持多继承,使用import来依赖管理配置-->
<scope>import</scope>
</dependency>
<!--导入 mybatis 启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
注意:如果是copy的pom文件,可能会碰到下面的这个提示。拷贝时带了特殊符号。在idea中将每行前面的空格删除。然后Alt+Ctrl+L格式化。提示信息:expected START_TAG or END_TAG not TEXT (position: TEXT seen ...<properties>\n \ua0\ua0\ua0\ua0<p...
最后删除 src 目录及子目录。
3)构建公共模块(microservice-cloud-02-api)
pom中可以看到它的parent为:
<parent> <groupId>com.run.springcloud</groupId> <artifactId>run-cloud-01</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../run-cloud-01/pom.xml</relativePath> </parent>ent>
1.创建product实体类:
package com.caxa.entities;
import java.io.Serializable;
public class Product implements Serializable {
public String pid;
public String productName;
public String dbSource;
public Product() {
}
public Product(String pid, String produceName, String dbSource) {
this.pid = pid;
this.productName = produceName;
this.dbSource = dbSource;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getProduceName() {
return productName;
}
public void setProduceName(String produceName) {
this.productName = produceName;
}
public String getDbSource() {
return dbSource;
}
public void setDbSource(String dbSource) {
this.dbSource = dbSource;
}
}
4)构建服务提供者
创建Module:microservice-cloud-03-provider-product-8001
配置 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>microservice-cloud-01</artifactId>
<groupId>com.run.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../microservice-cloud-01/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>microservice-cloud-03-provider-product-8001</artifactId>
<dependencies>
<dependency>
<groupId>com.run.springcloud</groupId>
<artifactId>microservice-cloud-02-api</artifactId>
<version>${project.version}</version>
</dependency>
<!--springboot web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis 启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<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>
</dependencies>
</project>
在 src/main/resources下新建application.yml文件,配置如下,注意其中的空格
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.mengxuegu.springcloud.entities # 所有Entity别名类所在包
mapper-locations: classpath:mybatis/mapper/**/*.xml # mapper映射文件
spring:
application:
name: microservice-product #这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动包
url: jdbc:mysql://127.0.0.1:3306/springcloud_db01?serverTimezone=GMT%2B8 # 数据库名称
username: root
password: root
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 150 # 等待连接获取的最大超时时间
创建mysql数据库脚本:
DROP DATABASE IF EXISTS springcloud_db01;
CREATE DATABASE springcloud_db01 CHARACTER SET UTF8;
USE springcloud_db01;
CREATE TABLE product
(
pid BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
product_name VARCHAR(50),
db_source VARCHAR(50)
);
INSERT INTO product(product_name,db_source) VALUES('格力空调',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('海尔冰箱',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('小短裙',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('羽绒服',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('韩版休闲鞋',DATABASE());
INSERT INTO product(product_name,db_source) VALUES('高贵鞋',DATABASE());
创建 Mapper 接口
package com.run.springcloud.mapper;
import com.run.springcloud.entities.Product;
import java.util.List;
public interface ProductMapper{
public Product findById(Long pid);
public List<Product> findAll();
public boolean addProduct(Product product);
}
Mybatis 相关配置
src/main/resources下新建 mybatis 目录后新建 mybatis.cfg.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="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
src/main/resources/mybatis 下新建 mapper 目录后新建 ProductMapper.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.run.springcloud.mapper.ProductMapper">
<select id="findById" resultType="Product" parameterType="Long">
select pid, product_name, db_source from product where pid=#{pid};
</select>
<select id="findAll" resultType="Product">
select pid, product_name, db_source from product;
</select>
<insert id="addProduct" parameterType="Product">
INSERT INTO product(product_name, db_source) VALUES(#{productName},
DATABASE());
</insert>
</mapper>
创建 服务层,服务接口:ProductService
package com.run.springcloud.service;
import com.run.springcloud.entities.Product;
import java.util.List;
public interface ProductService {
public Product findById(Long id);
public List<Product> findAll();
public boolean addProduct(Product product);
}
服务接口实现类 ProductServiceImpl
package com.run.springcloud.service.com.run.springcloud.service.impl;
import com.run.springcloud.mapper.ProductMapper;
import com.run.springcloud.entities.Product;
import com.run.springcloud.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Override
public Product findById(Long id) {
return productMapper.findById(id);
}
@Override
public List<Product> findAll() {
return productMapper.findAll();
}
@Override
public boolean addProduct(Product product) {
return productMapper.addProduct(product);
}
}
创建 控制层-提供者
package com.run.springcloud.controller;
import com.run.springcloud.entities.Product;
import com.run.springcloud.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.lang.reflect.Method;
import java.util.List;
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value = "/product/add", method = RequestMethod.POST)
public boolean add(@RequestBody Product product) {
return productService.addProduct(product);
}
@RequestMapping(value = "/product/get/{id}")
public Product get(@PathVariable("id") Long id) {
return productService.findById(id);
}
@RequestMapping(value = "/product/list", method = RequestMethod.GET)
public List<Product> list() {
return productService.findAll();
}
}
创建主启动类:ProductProvider_8001
package com.run.springcloud;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.run.springcloud.mapper")
@SpringBootApplication
public class ProductProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(ProductProvider_8001.class);
}
}
功能测试
get方法的直接在浏览器中测试。
http://localhost:8001/product/get/3
http://localhost:8001/product/list
注意add的post方法测试。使用postman测试。如下图:
创建服务消费者。microservice-cloud-04-consumer-product-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>microservice-cloud-01</artifactId>
<groupId>com.run.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../microservicecloud01/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>microservice-cloud-04-consumer-product-80</artifactId>
<dependencies>
<dependency>
<groupId>com.run.springcloud</groupId>
<artifactId>microservice-cloud-02-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
配置 application.yml 文件
server:
port: 80
自定义Rest相关配置类
在 com.run.springcloud.config 包下新建配置类: ConfigBean
向容器中注入 RestTemplate 组件, 提供了多种简单便捷的访问 Restful 服务的方法,是Spring提供的用于访
问Rest服务的客户端模板工具集。 (url, requestMap, ResponseBean.class)这三个参数分别代表:
REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
package com.run.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();
}
}
创建 控制层-消费者
在 com.run.springcloud.controller 包下新建消费者: ProductController_Consumer
package com.run.springcloud.controller;
import com.run.springcloud.entities.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class ProductController_Consumer {
private static final String REST_URL_PREFIX = "http://localhost:8001";
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value="consumer/product/add",method = RequestMethod.POST)
public boolean add(@RequestParam Product product){
return restTemplate.postForObject(REST_URL_PREFIX+"/product/add",product,Boolean.class);
}
@RequestMapping(value="consumer/product/get/{id}",method = RequestMethod.GET)
public Product get(@PathVariable("id") String id){
return restTemplate.getForObject(REST_URL_PREFIX+"/product/get/"+id,Product.class,);
}
@RequestMapping(value="consumer/product/list",method = RequestMethod.GET)
public List<Product> list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/product/list/",List.class);
}
}
创建 主启动类
package com.run.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProductConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(ProductConsumer_80.class, args);
}
}
测试:
http://localhost/consumer/product/get/3
http://localhost/consumer/product/list