什么是微服务?
- 微服务最早是于2014年3月25日由一个叫 Martin Flower 的国外人提出的, 他写的《Microservices》最早介绍的微服务的思想。
- 也有好多业界人士对其进行翻译,微服务介绍的翻译版
- 微服务的优势:
- 将复杂的业务拆分成多个小的业务,每个业务拆分成一个服务,将复杂的问题简单化,利于分工,降低新人的学习成本
- 微服务系统是分布式系统,业务与业务之间完全解耦,随着业务的增加可以根据业务再拆分,具有极强的横向扩展能力,面对高并发的场景可以将服务集群化部署,加强系统负载能力
- 服务间采用 HTTP 协议通信,服务与服务之间完全独立。每个服务可以根据业务场景选取合适的编程语言和数据库
- 微服务每个服务都是独立部署的,每个服务的修改和部署对其他服务没有影响
什么是Spring Cloud?
- SpringCloud是关注于全局的微服务协调治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来
- 分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的结合体,俗称为微服务全家桶
快速入门
1. 步骤一:创建空的父项目
-
先建一个普通的 maven 项目
-
删掉它的 src 目录(使它变为一个父项目)
-
添加 pom 依赖
<?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>JavaWeb</groupId>
<artifactId>spring-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring-cloud-api</module>
<module>spring-eureka-7001</module>
<module>spring-provider-dept-8001</module>
</modules>
<!--打包方式为pom方式-->
<packaging>pom</packaging>
<!--可以动态修改版本号,工作中多是这种pom文件-->
<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>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<logback.version>1.2.3</logback.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>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>1.3.2</version>
</dependency>
<!--日志测试~-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2. 步骤二:编写只有pojo类的子项目
-
创建只存放 pojo 类的的子项目
-
添加 pom 依赖
<?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>spring-cloud</artifactId> <groupId>JavaWeb</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>spring-cloud-api</artifactId> <!--当前的Module自己需要的依赖,如果父依赖中已经配置了版本。这里就不用写了--> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
-
编写实体类: @Accessors(chain = true) 这个注解可以实现链式写法
package cn.edu.xiyou.pojo; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import java.math.BigInteger; @Data @NoArgsConstructor @Accessors(chain = true) //链式写法 public class Dept { private BigInteger deptno; private String dname; private String db_Source; public Dept(String dname) { this.dname = dname; } }
3. 步骤三: 编写服务端的子项目
-
创建 服务端的子项目 ,名字最好带有端口号
-
引入 pom 依赖
<?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>spring-cloud</artifactId> <groupId>JavaWeb</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>spring-provider-dept-8001</artifactId> <dependencies> <dependency> <groupId>JavaWeb</groupId> <artifactId>spring-cloud-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>
-
编写mapper接口,放在mapper包下
package cn.edu.xiyou.mapper; import cn.edu.xiyou.pojo.Dept; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.math.BigInteger; import java.util.List; @Mapper @Repository public interface deptMapper { Dept selectById(@Param("id") BigInteger id); List<Dept> selectAll(); Boolean addDept(Dept dept); }
-
在 resources 包下建 mybatis 包,其下建一个mybatis-config.xml的核心配置文件和一个mapper的包(里面放mapper接口对应的xml配置文件)
-
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>
-
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="cn.edu.xiyou.mapper.deptMapper"> <select id="selectById" resultType="Dept"> select * from db01.dept where deptno=#{id} </select> <select id="selectAll" resultType="Dept"> select * from db01.dept; </select> <insert id="addDept"> insert into db01.dept (dname, db_Source) VALUES (#{dname},DATABASE()); </insert> </mapper>
-
-
编写service层代码(在service包下写服务)
-
service接口
package cn.edu.xiyou.service; import cn.edu.xiyou.pojo.Dept; import java.math.BigInteger; import java.util.List; public interface deptService { Dept selectById(BigInteger id); List<Dept> selectAll(); Boolean addDept(Dept dept); }
-
service接口实现类
package cn.edu.xiyou.service; import cn.edu.xiyou.mapper.deptMapper; import cn.edu.xiyou.pojo.Dept; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigInteger; import java.util.List; @Service public class deptServiceImpl implements deptService{ @Autowired private deptMapper mapper; @Override public Dept selectById(BigInteger id) { return mapper.selectById(id); } @Override public List<Dept> selectAll() { return mapper.selectAll(); } @Override public Boolean addDept(Dept dept) { return mapper.addDept(dept); } }
-
-
编写controller层(在controller包下写访问路径)
package cn.edu.xiyou.controller; import cn.edu.xiyou.pojo.Dept; import cn.edu.xiyou.service.deptServiceImpl; 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; //提供rest服务 @RestController public class deptController { @Autowired private deptServiceImpl service; @GetMapping("/dept/get/{id}") //@PathVariable 注解可以进行restful传递参数 public Dept selectById(@PathVariable("id") Long id){ return service.selectById(id); } @GetMapping("/dept/list") public List<Dept> selectAll(){ return service.selectAll(); } @PostMapping("/dept/add") public boolean addDept(Dept dept){ return service.addDept(dept); } }
-
编写application.yml核心配置文件
server: port: 8001 #mybatis配置,相当于build设置 mybatis: type-aliases-package: cn.edu.xiyou.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 driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8 username: root password: 123456
-
自实现一个启动类
package cn.edu.xiyou; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; //开启类 @SpringBootApplication public class Provider_8001 { public static void main(String[] args) { SpringApplication.run(Provider_8001.class,args); } }
-
启动自实现的启动类,访问地址即可
4. 步骤四:编写客户端的子项目
-
创建 客户端的子项目 ,名字最好带有端口号(最好是 80 端口)
-
引入依赖
<dependencies> <dependency> <groupId>JavaWeb</groupId> <artifactId>spring-cloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependenc)y> <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>
-
思考几个问题:
-
客户端需不需要 service 层?
答案肯定是不用,客户端只需要 controller 层就好。
-
客户端不需要 service 层,怎么才能实现对微服务的调用?
-
方式一:RestTemplate
-
方式二: Feign
-
-
RestTemplate 和 Feign 的区别?
- …
-
-
编写 config 层,自定义config类自动装配Rest模板
package cn.edu.xiyou.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class configBean { //自动装配 RestTemplate (Rest模板) @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); }; }
-
编写 controller 层,自动注入Rest模板,实现对微服务的调用
package cn.edu.xiyou.controller; import cn.edu.xiyou.pojo.Dept; 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 org.springframework.web.client.RestTemplate; import java.util.List; //消费者不应该有 service 层 @RestController public class deptController { @Autowired private RestTemplate restTemplate; //url前缀 private static final String REST_URL_PREFIX = "http://localhost:8001"; @GetMapping("/consumer/dept/get/{id}") public Dept selectById(@PathVariable("id") Long id){ return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class); } @GetMapping("/consumer/dept/list") public List<Dept> selectAll(){ return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class); } @PostMapping("/consumer/dept/add") public Boolean add(Dept dept){ return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class); } }
-
编写application.yml核心配置文件
server: port: 80
-
自实现启动类
package cn.edu.xiyou; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Consumer_80 { public static void main(String[] args) { SpringApplication.run(Consumer_80.class,args); } }
-
启动自实现的启动类,访问地址即可,80 端口可以省略不写,HTTP请求默认是 80 端口