最开始我们用的都是单一架构(all in one),为什么会演变成垂直架构应用(vertical application)呢,由于我们最开始的用户本来就少,10几年前20年前貌似电脑没有多少人家有的把,网页也都是静态页面,访问量根本不用担心,而且所有业务都在同一里面,当我们修改了一个地方,哪怕仅仅是一个页面的一个汉字,整个系统都要重新部署修改,系统模块之间耦合度太高,慢慢的就想到能不能把系统各个模块分开呢,也就出现了垂直架构,但是使用着慢慢也发现,尽管界面+业务逻辑实现了逻辑的分离,但是应用不可能完全独立,大量的应用之间需要交互,耦合度还是比较高,而且用户也逐渐增多,部署在一个机器上承受不了这么大的访问量,这也就出现了SOA架构,SOA架构对系统又进行了粒度细化,将一个大的系统服务拆分成多个子服务,部署到不同机器上,通过rpc或者webservice实现模块之间的交互,而现在逐渐演变的微服务架构(microservice),则是将服务更加颗粒化,使之耦合度更低,因此微服务是现在的趋势,而springcloud则是微服务的一个代表。
由于上面我们所说的,将一个大的系统服务拆分成多个小的子系统,那么系统之间通信就比较困难,我们以前通过webservice啥的进行通信,这样我们就需要知道要调用的服务的ip还有端口号信息,而且例如我子服务a知道b服务的ip端口,但是当b服务的ip或者端口修改后,我们还需要修改子服务a的信息,当我们系统足够大,并且子服务足够多的时候,系统之间通信就变得非常乱,不好维护,而eureka则解决了这一问题,所有子服务都将自己的ip端口信息都注册给eureka,由eureka帮我们实现统一管理,比如服务a需要与服务b进行通信,服务a不需要知道b服务的ip及端口了,因为服务b已经将自己的信息注册到了eureka上了,我们只需要在eureka上查就可以了,当服务b信息修改了,服务b会自动提交给eureka最新的信息,服务a完全不受影响。
搭建项目结构
在此之前,我们可以先把简单的项目结构搭建起来,无非就是用maven创建多模块项目,不会的可以参考 maven多模块搭建步骤 这篇文章,完成后大致结构如下图所示:
由于demo_parent作为整个项目的父项目,作用: 同一项目版本和公共依赖的引入,所以先将依赖在pom文件中引入。
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 <groupId>com.lytw13</groupId> 7 <artifactId>demo_parent</artifactId> 8 <packaging>pom</packaging> 9 <version>1.0-SNAPSHOT</version> 10 <modules> 11 <module>../demo_api</module> 12 <module>../demo_sys</module> 13 <module>../demo_configuration</module> 14 <module>../demo_web</module> 15 </modules> 16 <!-- 统一项目版本信息 --> 17 <properties> 18 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 19 <maven.compiler.encoding>UTF-8</maven.compiler.encoding> 20 <java.version>11</java.version> 21 <maven.compiler.source>11</maven.compiler.source> 22 <maven.compiler.target>11</maven.compiler.target> 23 <spring-boot-version>2.1.9.RELEASE</spring-boot-version> 24 <spring-cloud.version>Greenwich.SR3</spring-cloud.version> 25 <project.version>1.0-SNAPSHOT</project.version> 26 </properties> 27 <dependencyManagement> 28 <dependencies> 29 <!-- springboot --> 30 <dependency> 31 <groupId>org.springframework.boot</groupId> 32 <artifactId>spring-boot-dependencies</artifactId> 33 <version>${spring-boot-version}</version> 34 <type>pom</type> 35 <scope>import</scope> 36 </dependency> 37 <!-- springcloud --> 38 <dependency> 39 <groupId>org.springframework.cloud</groupId> 40 <artifactId>spring-cloud-dependencies</artifactId> 41 <version>${spring-cloud.version}</version> 42 <type>pom</type> 43 <scope>import</scope> 44 </dependency> 45 </dependencies> 46 </dependencyManagement> 47 <dependencies> 48 <!-- web --> 49 <dependency> 50 <groupId>org.springframework.boot</groupId> 51 <artifactId>spring-boot-starter-web</artifactId> 52 </dependency> 53 <!-- 集成了测试相关依赖,例如junit --> 54 <dependency> 55 <groupId>org.springframework.boot</groupId> 56 <artifactId>spring-boot-starter-test</artifactId> 57 <scope>test</scope> 58 </dependency> 59 <!--监控信息展示--> 60 <dependency> 61 <groupId>org.springframework.boot</groupId> 62 <artifactId>spring-boot-starter-actuator</artifactId> 63 </dependency> 64 <!--json--> 65 <dependency> 66 <groupId>com.alibaba</groupId> 67 <artifactId>fastjson</artifactId> 68 <version>1.2.62</version> 69 </dependency> 70 <!-- email--> 71 <dependency> 72 <groupId>org.springframework.boot</groupId> 73 <artifactId>spring-boot-starter-mail</artifactId> 74 </dependency> 75 </dependencies> 76</project> 复制代码
eureka注册中心搭建步骤
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>demo_parent</artifactId> 7 <groupId>com.lytw13</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 <artifactId>demo_configuration</artifactId> 12 <packaging>pom</packaging> 13 <modules> 14 <module>../demo_eurekaServer</module> 15 <module>../demo_config</module> 16 <module>../demo_zuul</module> 17 </modules> 18 <dependencies> 19 <!-- eureka服务端 --> 20 <dependency> 21 <groupId>org.springframework.cloud</groupId> 22 <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> 23 </dependency> 24 </dependencies> 25</project> 复制代码
(1) 添加eureka主启动类
1package com.lytw13.demo; 2import org.springframework.boot.SpringApplication; 3import org.springframework.boot.autoconfigure.SpringBootApplication; 4import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 5 6@EnableEurekaServer 7@SpringBootApplication 8public class EurekaApplication { 9 public static void main(String[] args) { 10 SpringApplication.run(EurekaApplication.class,args); 11 } 12} 复制代码
(2) 新建eureka配置文件application.yml。
1server: 2 port: 6001 3 4spring: 5 application: 6 name: eureka 7eureka: 8 instance: 9 ip-address: true 10 instance-id: eureka01 11 client: 12 fetch-registry: false 13 register-with-eureka: false 14 service-url: 15 defaultZone: http://localhost:6001/eureka/ 复制代码
(1) 启动项目。
(2) 启动完成后,访问配置的路径 localhost:端口号 ,查看是否启动成功。
服务注册到eureka
现在尽管我们有了eureka注册中心,但是没有任何服务,我们需要将我们项目中的服务注册到注册中心上,这样我们才能实现服务之前的通信。
这里以demo_sys_user用户服务为例,其他步骤类似。因为demo_sys_user是demo_sys的子模块,所以我们只需要将依赖添加到demo_sys上即可,这样demo_sys下的其他子模块也就可以同时使用,比如连接数据库的相关依赖,这样就实现了依赖的统一管理。
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>demo_parent</artifactId> 7 <groupId>com.lytw13</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>demo_sys</artifactId> 13 <packaging>pom</packaging> 14 <modules> 15 <module>../demo_sys_user</module> 16 <module>../demo_sys_role</module> 17 <module>../demo_sys_menu</module> 18 <module>../demo_sys_notice</module> 19 </modules> 20 <dependencies> 21 <!-- 自定义api --> 22 <dependency> 23 <groupId>com.lytw13</groupId> 24 <artifactId>demo_api</artifactId> 25 <version>1.0-SNAPSHOT</version> 26 </dependency> 27 <!-- eureka客户端 --> 28 <dependency> 29 <groupId>org.springframework.cloud</groupId> 30 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 31 </dependency> 32 <!-- 数据库相关 --> 33 <dependency> 34 <groupId>mysql</groupId> 35 <artifactId>mysql-connector-java</artifactId> 36 </dependency> 37 <dependency> 38 <groupId>org.mybatis.spring.boot</groupId> 39 <artifactId>mybatis-spring-boot-starter</artifactId> 40 <version>2.1.1</version> 41 </dependency> 42 <dependency> 43 <groupId>com.alibaba</groupId> 44 <artifactId>druid-spring-boot-starter</artifactId> 45 <version>1.1.20</version> 46 </dependency> 47 </dependencies> 48</project> 复制代码
1package com.lytw13.demo; 2 3import org.springframework.boot.SpringApplication; 4import org.springframework.boot.autoconfigure.SpringBootApplication; 5import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 7@SpringBootApplication 8@EnableEurekaClient //开启eureka客户端 9public class UserApplication { 10 public static void main(String[] args) { 11 SpringApplication.run(UserApplication.class,args); 12 } 13} 复制代码
1server: 2 port: 7001 3 4spring: 5 application: 6 name: user 7 datasource: 8 driver-class-name: com.mysql.cj.jdbc.Driver 9 url: jdbc:mysql://localhost:3306/db_demo_sys?serverTimezone=Asia/Shanghai 10 username: root 11 password: 123456 12 type: com.alibaba.druid.pool.DruidDataSource 13 druid: 14 # 连接池的配置信息 15 # 初始化大小,最小,最大 16 initial-size: 5 17 min-idle: 5 18 maxActive: 20 19 # 配置获取连接等待超时的时间 20 maxWait: 60000 21 filters: stat,wall,slf4j 22 23mybatis: 24 mapper-locations: classpath:mapper/*.xml 25 config-location: classpath:mybatis/mybatis.xml 26 27 28 29info: 30 name: lytw13 31 web: lytw13.top 32eureka: 33 client: 34 service-url: 35 defaultZone: http://localhost:6001/eureka/ 36 fetch-registry: true 37 register-with-eureka: true 38 instance: 39 instance-id: user01 40 ip-address: true 41 42logging: 43 level: 44 com: 45 lytw13: 46 microservice: 47 dao: debug 复制代码
启动该项目,查看eureka上是否有了该服务。
可以看到user已经注册到了eureka上,其他服务注册相同,不再赘述。
eureka服务注册的大概结构如下:
尽管这样,还是需要修改一些内容,我们将服务注册到eureka上,不仅仅是为了注册到上面,而是为了让其他服务可以调用,而我们上面的项目结构图可以看到demo_api项目是为了将公共接口,实体类,工具类抽取出来,因此我们可以将userservice接口还有实体类工具类的创建到demo_api里,下面接口代码列在下面,实体类就不用了吧,方法的返回值BaseResult是自己封装好的,当然可以直接返回实体类或者list啥的,这里是为了统一返回结果,方便处理。
1package com.lytw13.demo.service; 2 3import com.lytw13.demo.model.BaseResult; 4import org.springframework.web.bind.annotation.*; 5 6@RequestMapping(value = "/user") 7public interface UserService { 8 @GetMapping("/get/{id}") 9 public BaseResult get(@PathVariable("id") Integer id); 10 @GetMapping("/list") 11 public BaseResult list(); 12} 复制代码
1package com.lytw13.demo.model; 2 3import lombok.*; 4 5@AllArgsConstructor 6@NoArgsConstructor 7@Getter 8@Setter 9@ToString 10public class BaseResult { 11 private Integer resultCode; 12 private String resultMsg; 13 private Object resultData; 14} 复制代码
1package com.lytw13.demo.utils; 2 3 4import com.lytw13.demo.model.BaseResult; 5 6public class ResponseResult { 7 public BaseResult setResult(Integer resultCode, String resultMsg, Object resultData){ 8 return new BaseResult(resultCode, resultMsg, resultData); 9 } 10 public BaseResult setResultSuccess(Object resultData) { 11 return new BaseResult(200,"success",resultData); 12 } 13 public BaseResult setResultFail(String msg) { 14 return new BaseResult(400,msg,null); 15 } 16} 复制代码
接着我们只需要编写demo_sys_user类了,具体无非也是实现上面的UserService接口,通过usermapper调用数据库,这里不再赘述了。编写完成后启动项目,通过application.yml中设置的端口访问方法,看看能否访问成功。
以前我们通过ip加端口访问,可以通过spring中的自带的RestTemplete进行访问。
这里我们使用demo_web作为消费者,调用demo_sys_user服务。
1package com.lytw13.demo.controller; 2 3import com.lytw13.demo.model.BaseResult; 4import org.springframework.beans.factory.annotation.Autowired; 5import org.springframework.web.bind.annotation.*; 6import org.springframework.web.client.RestTemplate; 7 8@RestController 9@RequestMapping("/user") 10public class UserController { 11 @Autowired 12 RestTemplate restTemplate; 13 @GetMapping("getUser") 14 public BaseResult getUser(Integer id) { 15 BaseResult baseResult = restTemplate.getForObject("http://localhost:7001/user/get/"+id, BaseResult.class); 16 return baseResult; 17 } 18} 复制代码
我们可以看到端口还有ip都是暴露的,这样肯定是不好的,因此我们就需要添加Ribbon来实现本地负载均衡,将上面的ip端口用在eureka注册中心的服务名来写。
1package com.lytw13.demo.controller; 2 3import com.lytw13.demo.model.BaseResult; 4import org.springframework.beans.factory.annotation.Autowired; 5import org.springframework.web.bind.annotation.*; 6import org.springframework.web.client.RestTemplate; 7 8@RestController 9@RequestMapping("/user") 10public class UserController { 11 @Autowired 12 RestTemplate restTemplate; 13 @GetMapping("getUser") 14 public BaseResult getUser(Integer id) { 15 BaseResult baseResult = restTemplate.getForObject("http://USER/user/get/"+id, BaseResult.class); 16 return baseResult; 17 } 18} 复制代码
eureka服务之间调用的大概结构如下: