如何搭建一个简单的微服务?
我们本文,只介绍如何搭建一个微服务架构,其中微服务的理论知识见另一篇文章:
1、创建父工程(之后所有的服务,均在父工程下创建),引入在pom文件引入依赖
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>com.mk</groupId> 8 <artifactId>spring_cloud</artifactId> 9 <version>1.0-SNAPSHOT</version> 10 11 <modules> 12 <module>User-Service</module> 13 <module>consumer-user</module> 14 <module>Euraka-service</module> 15 <module>getAwayService</module> 16 <module>myConfig</module> 17 </modules> 18 <packaging>pom</packaging> 19 20 <properties> 21 <java.version>1.8</java.version> 22 <spring-cloud.version>Greenwich.SR1</spring-cloud.version> 23 <mapper.starter.version>2.1.5</mapper.starter.version> 24 <mysql.version>5.1.46</mysql.version> 25 </properties> 26 27 28 <parent> 29 <groupId>org.springframework.boot</groupId> 30 <artifactId>spring-boot-starter-parent</artifactId> 31 <version>2.1.2.RELEASE</version> 32 <relativePath/> 33 34 </parent> 35 36 <dependencyManagement> 37 <dependencies> 38 <!-- springCloud --> 39 <dependency> 40 <groupId>org.springframework.cloud</groupId> 41 <artifactId>spring-cloud-dependencies</artifactId> 42 <version>${spring-cloud.version}</version> 43 <type>pom</type> 44 <scope>import</scope> 45 </dependency> 46 <!-- 通用Mapper启动器 --> 47 <dependency> 48 <groupId>tk.mybatis</groupId> 49 <artifactId>mapper-spring-boot-starter</artifactId> 50 <version>${mapper.starter.version}</version> 51 </dependency> 52 <!-- mysql驱动 --> 53 <dependency> 54 <groupId>mysql</groupId> 55 <artifactId>mysql-connector-java</artifactId> 56 <version>${mysql.version}</version> 57 </dependency> 58 59 60 </dependencies> 61 </dependencyManagement> 62 <dependencies> 63 <dependency> 64 <groupId>org.projectlombok</groupId> 65 <artifactId>lombok</artifactId> 66 </dependency> 67 </dependencies> 68 <build> 69 <plugins> 70 <plugin> 71 <groupId>org.springframework.boot</groupId> 72 <artifactId>spring-boot-maven-plugin</artifactId> 73 </plugin> 74 </plugins> 75 </build> 76 </project>
2、服务提供者:我们的服务是,根据id查询一个user,所以就会引入一些数据库相关的依赖
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>spring_cloud</artifactId> 7 <groupId>com.mk</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 11 12 <modelVersion>4.0.0</modelVersion> 13 14 <artifactId>User-Service</artifactId> 15 16 <properties> 17 <maven.compiler.source>8</maven.compiler.source> 18 <maven.compiler.target>8</maven.compiler.target> 19 </properties> 20 21 <dependencies> 22 <dependency> 23 <groupId>org.springframework.boot</groupId> 24 <artifactId>spring-boot-starter-web</artifactId> 25 </dependency> 26 27 <dependency> 28 <groupId>org.springframework.boot</groupId> 29 <artifactId>spring-boot-starter-test</artifactId> 30 </dependency> 31 32 33 <!-- 通用Mapper启动器 --> 34 <dependency> 35 <groupId>tk.mybatis</groupId> 36 <artifactId>mapper-spring-boot-starter</artifactId> 37 </dependency> 38 39 40 <!-- mysql驱动 --> 41 <dependency> 42 <groupId>mysql</groupId> 43 <artifactId>mysql-connector-java</artifactId> 44 </dependency> 45 46 <dependency> 47 <groupId>org.springframework.cloud</groupId> 48 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 49 </dependency> 50 51 <dependency> 52 <groupId>org.springframework.cloud</groupId> 53 <artifactId>spring-cloud-starter-config</artifactId> 54 </dependency> 55 </dependencies> 56 57 58 </project>
2.1:实体类:其中Data注解,减少代码量
2.2:dao层:使用tkMybatis,所以就不需要我们自己写单表的一些语句
2.3:service层:
2.4:controller层:
2.5:启动类:
2.6:yml配置文件(配有注释)
3.1:服务消费者:因为客户端,不需要操作数据库,所以只需要实体类和控制层就行。
3.2:pom依赖文件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>spring_cloud</artifactId> 7 <groupId>com.mk</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>consumer-user</artifactId> 13 14 <properties> 15 <maven.compiler.source>8</maven.compiler.source> 16 <maven.compiler.target>8</maven.compiler.target> 17 </properties> 18 19 20 <dependencies> 21 <dependency> 22 <groupId>org.springframework.boot</groupId> 23 <artifactId>spring-boot-starter-web</artifactId> 24 </dependency> 25 26 27 <!--euraka客户端--> 28 <dependency> 29 <groupId>org.springframework.cloud</groupId> 30 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 31 </dependency> 32 33 <!--熔断器--> 34 <dependency> 35 <groupId>org.springframework.cloud</groupId> 36 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> 37 </dependency> 38 39 <!--feign代理--> 40 <dependency> 41 <groupId>org.springframework.cloud</groupId> 42 <artifactId>spring-cloud-starter-openfeign</artifactId> 43 </dependency> 44 </dependencies> 45 46 </project>
3.3:实体类:因为是客户端,所以就不需要那些jpa注解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.mk.consumer.pojo; 2 3 import lombok.Data; 4 5 @Data 6 public class User { 7 private Long id; 8 private String userName; 9 private String password; 10 private String name; 11 private long age; 12 private long sex; 13 private java.sql.Date birthday; 14 private java.sql.Date created; 15 private java.sql.Date updated; 16 private String note; 17 18 }
3.4:controller层:(这里我们采用截图,因为后面会对这里进行改造)
3.5:配置文件:
3.6:启动类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.mk.consumer; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 6 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 import org.springframework.cloud.client.loadbalancer.LoadBalanced; 8 import org.springframework.cloud.openfeign.EnableFeignClients; 9 import org.springframework.context.annotation.Bean; 10 import org.springframework.web.client.RestTemplate; 11 12 @SpringBootApplication 13 @EnableDiscoveryClient 14 @EnableCircuitBreaker //开启熔断器 15 @EnableFeignClients //开启feign组件功能 16 public class Application { 17 public static void main(String[] args) { 18 SpringApplication.run(Application.class, args); 19 } 20 21 22 /*spring提供的模板,对http进行封装*/ 23 @Bean 24 @LoadBalanced //负载均衡 25 public RestTemplate restTemplate(){ 26 return new RestTemplate(); 27 } 28 }
4.1Eureka注册中心;既然后我们客户端和服务端都有了,那么我们还需要搭建自己的Eureka注册中心,来发现服务,引入依赖
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>spring_cloud</artifactId> 7 <groupId>com.mk</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>Euraka-service</artifactId> 13 14 <properties> 15 <maven.compiler.source>8</maven.compiler.source> 16 <maven.compiler.target>8</maven.compiler.target> 17 </properties> 18 19 <dependencies> 20 <dependency> 21 <groupId>org.springframework.cloud</groupId> 22 <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> 23 </dependency> 24 </dependencies> 25 26 27 28 </project>
4.2:配置文件:(配有注解)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #端口号 2 server: 3 port: 10010 4 5 #服务名,因为Eureka本身也是一个服务 6 spring: 7 application: 8 name: eureka 9 10 #服务注册中心的位置 11 eureka: 12 client: 13 service-url: 14 defaultZone: http://127.0.0.1:10010/eureka 15 instance: 16 17 # 不注册自己 18 register-with-eureka: false 19 # 不拉取服务 20 fetch-registry: false
4.3:启动类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.mk.eureka; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 7 @SpringBootApplication 8 @EnableEurekaServer 9 public class Application { 10 public static void main(String[] args) { 11 SpringApplication.run(Application.class, args); 12 } 13 }
以上我们的客户端,服务端,注册中心都配置完毕,那么我们启动服务看看。
访问Eureka注册中心:注意端口号
访问我们的服务:(传入id为2),可以看到,返回一个json数据
以上就是,我们传入正确的id,假如,我们传入的id,在数据库中,不存在怎么办呢。经实践发现,服务就会一直等待,那么我们后续的其他的请求就会阻塞,这样我们后续用户的请求也会阻塞。
5.Spring Cloud为我们提供了Hystrix组件:特地处理我们服务时间超时,然后我们可以回显给用户一些有好的提示。
5.1:添加hystrix依赖
5.2:在启动类上添加开启@EnableCircuitBreaker的注解
5.3.编写我们的降级逻辑,也就是给用户的提示
5.4.测试降级逻辑,为了确保一定能够满足服务降级的条件(1.该服务的线程池满,2.服务超时(Hystrix的默认超时时长为1)),我们就不启动服务端,那么客户端的请求一定超时,就会触发服务降级。下面是测试结果
从上面可以看出,我们的服务,虽然没有得到应该的结果,但是我们的线程得到释放,并不会影响其他的服务。这就是我们服务降级的一个流程。
那么上面服务降级的方式,我们每增加一个方法是不是,就需要我们在该方法上进行注入呢,还有另外一种,更加简便的方式,就注解在类上,就不用每一个方法都去注入了
以上我们就搭建了一个简易的微服务,但是还存在一些问题。比如我的3.4步骤中,可以看到我们的url被写死,那么我们后期的需求改变或者说是拓展其他的功能,那么显然是不方便的。
我们SpringCloud给我们提供了一个叫做feign的组件,是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign, 我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。下面,我们就对客户端进行优化。
6.Feign声明式服务,
6.1.pom文件依赖,添加如下
6.2我们添加日志配置类,方便我们对请求的参数,请求体进行查看。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.mk.consumer.logger; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignLogger { @Bean public Logger.Level feignLoggerLevel(){ //日志级别是记录所有(包括请求的行,请求体,) return Logger.Level.FULL; } }
6.3.下面我们添加我们的客户端控制层的接口,
6.4.我们改造后的控制层,就变得优雅了许多,仿佛就是在调用我们的本地方法。
6.5.测试结果
从结果可以看出,我们同样可以得到结果,这就是feign组件的作用,方便我们服务的调用,得到与调用本地方法一致的编码体验。
6.6.feign对hystrix的支持,feign中有对hystrix的支持,我们只需要定义一个类,实现我们6.3中的客户端接口,然后再客户端中通过注解,添加即可。下面是降级逻辑
6.7.下面我们看一下测试结果,触发条件和之前的hystrix一样
7. 以上就是,feign的使用。其实feign就是简化我们内部的,服务与服务之间的调用,那么我们服务暴露给对外的api接口,应该怎样解决呢?同样Spring Cloud提供了Gateway网关组件。
我们外部的请求,首先就会通过网关的过滤,鉴权,然后才能请求我们内部的服务。下面我们就来配置我们的网关。
7.1,新建一个model,引入依赖
7.2.编写启动类
7.3,编写配置文件,Gateway,重点就是配置文件
7.4下面我们,启动服务,看看效果
8.上面就是Gateway的简单应用,当然,网关还有一些添加前缀,去除前缀,过滤器的一些使用,大家可以自己去探索。大家肯定会发现,我们整个微服务搭建下来,配置文件超级多,而且以后开发,
所有的配置文件肯定也不是在一台电脑上,所以有的配置文件,就访问不了,没事,SpringCloud也为我们提供了SpringCloud config分布式配置中心。
8.1.既然要有git仓库,那就自己准备一个,我就不粘贴步骤了,唯一需要注意的是这个配置文件的命名方式,就比如我的配置文件的名字是:“userService-dev.yml”,userService就代表服务名,-这是分隔符,dev就代表是开发环境。
然后就是将我们的userService的配置文案上传到gitee的新建文件,(只截取了一部分)
8.2新建一个model,引入依赖
8.3编写启动类
8.4编写配置文件
8.5下面我们就通过本地访问git上的配置文件
这就是我们之前准备的在仓库中的配置文件,这样我们就可以通过配置中心进行访问。
8.6.既然可以通过配置中心访问,下面我们就对我们的服务端的配置文件(就是之前的UserService)进行修改,注意配置文件名改为bootstrap.yml,因为bootstrap.yml文件是Spring Boot的默认配置文件,而且其加载的时间相比于application.yml更早。 application.yml和bootstrap.yml虽然都是Spring Boot的默认配置文件,但是定位却不相同。bootstrap.yml 可以理解成系统级别的一些参数配置,这些参数一般是不会变动的
8.7.我们重启UserService服务。发现UserService仍然存在,说明能够读取到配置中心的文件。
9.既然,我们在git配置了配置文件,那么如果,我们在git上进行了修改,那么本地的配置文件会不会自己刷新呢,显然是不会的。我们的Spring cloud也为我们提供了叫做:Spring cloud bus服务总线,专门用于同步git上配置文件的改变。下面我们进行测试。
9.1. 在8.2中增加如下依赖,
9.3:在8.3增加如下配置
9.4.在往服务提供者中增加如下依赖:
9.5.修改2.4的controller为:
9.6.测试,先正常访问
9.7.控制台输出的是:
9.8.我们修改name为mk
9.9.利用我们postman工具,发送我们在8.3中暴露的api,(注意我们需要提前安装\otp_win64_23.0.exe和rabbitmq-server-3.8.5.exe),因为rabbitmq使用otp语言实现
9.10.我们再次访问9.6的url,查看控制台的输出变为我们刚刚更新的mk
就实现了我们配置中心改变,同步到本地的功能。
下面就是SpringCloud的完整体系架构图
整个微服务搭建完毕,各个组件的功能总结见另外一篇文章:https://www.cnblogs.com/kunmin/p/15108090.html