此博客用于个人学习,来源于网上,对知识点进行一个整理。
1. 广告系统主工程:
创建 project:imooc-ad-spring-cloud,采用 maven 工程的方式创建。
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.imooc.ad</groupId>
<artifactId>imooc-ad</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<name>imooc-ad-spring-cloud</name>
<description>Project For Imooc Ad SpringCloud</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
<!--自定义元素,管理通用属性-->
<properties>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--统一包管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--远程仓库-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
2. Eureka Server:
在 imooc-ad-spring-cloud 下,创建 module:ad-eureka。
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">
<!-- 指定父pom, 在父pom中配置子模块的公共依赖 -->
<parent>
<artifactId>imooc-ad</artifactId>
<groupId>com.imooc.ad</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<!-- 当前项目/模块的坐标, groupId从父模块中继承 -->
<artifactId>ad-eureka</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 模块名及描述信息 -->
<name>ad-eureka</name>
<description>Spring Cloud Eureka</description>
<!-- eureka server: 提供服务发现与服务注册 -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<!--
SpringBoot的Maven插件, 能够以Maven的方式为应用提供SpringBoot的支持,可以将
SpringBoot应用打包为可执行的jar或war文件, 然后以通常的方式运行SpringBoot应用
-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.1 启动类:
@EnableEurekaServer
@SpringBootApplication
public class EurekaAppliction {
public static void main(String[] args) {
SpringApplication.run(EurekaAppliction.class,args);
}
}
2.2 单节点部署:
先部署单节点的情况,后面再进行多节点的部署。
application.yml:
spring:
application:
name: ad-eureka
server:
port: 8000
eureka:
instance:
hostname: localhost
client: # 单节点设置为 false
fetch-registry: false
register-with-eureka: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
2.3 多节点部署:
进行多节点的部署,分别为 server1,server2,server3,对应端口 8000-8002。
改造 yml 文件:
---
spring:
application:
name: ad-eureka
profiles: server1
server:
port: 8000
eureka:
instance:
hostname: server1
prefer-ip-address: false
client:
service-url:
defaultZone: http://server2:8001/eureka/,http://server3:8002/eureka/
---
spring:
application:
name: ad-eureka
profiles: server2
server:
port: 8001
eureka:
instance:
hostname: server2
prefer-ip-address: false
client:
service-url:
defaultZone: http://server1:8000/eureka/,http://server3:8002/eureka/
---
spring:
application:
name: ad-eureka
profiles: server3
server:
port: 8002
eureka:
instance:
hostname: server3
prefer-ip-address: false
client:
service-url:
defaultZone: http://server1:8000/eureka/,http://server2:8001/eureka/
注意:在部署多节点的时候,需要对前面单节点部署的代码进行注释,避免由于优先加载前面部分代码导致的错误。
3. 微服务架构及网关组件介绍:
微服务,似乎也是服务,都是对系统进行拆分。因此两者非常容易混淆,但其实却有一些差别:
微服务的特点:
- 单一职责:微服务中每一个服务都对应唯一的业务能力,做到单一职责。
- 微:微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。
- 面向服务:面向服务是说每个服务都要对外暴露 Rest 风格服务接口 API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供 Rest 的接口即可。
- 自治:自治是说服务间互相独立,互不干扰。
- 团队独立:每个服务都是一个独立的开发团队,人数不能过多。
- 技术独立:因为是面向服务,提供 Rest 接口,使用什么技术没有别人干涉。
- 前后端分离:采用前后端分离开发,提供统一 Rest 接口,后端不用再为 PC、移动段开发不同接口。
- 数据库分离:每个服务都使用自己的数据源。
- 部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护。
这里只是对微服务讲一个大概,更多更具体的建议看下面两篇博客:
3.1 微服务架构的两种方式:
- 点对点模式:服务之间直接调用,每个微服务都开放 Rest API,并调用其他微服务的接口。
- API-网关方式:业务接口通过 API 网关暴露,是所有客户端接口的唯一入口,微服务之间的通信也是通过 API 网关。
3.2 Zuul 的生命周期:
服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供 REST API 的过程中,除了具备服务路由、均衡负载功能之外,它还具备了权限控制等功能。Spring Cloud Netflix 中的 Zuul 就担任了这样的一个角色,为微服务架构提供了前门保护的作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主体能够具备更高的可复用性和可测试性。
这张是 Zuul 官网提供的请求生命周期图,清晰的表现了一个请求在各个过滤器的执行顺序。
正常流程:
- 请求到达首先会经过 pre 类型过滤器,而后到达 route 类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达 post 过滤器。而后返回响应。
异常流程:
- 整个过程中,pre 或者 route 过滤器出现异常,都会直接进入 error 过滤器,在 error 处理完毕后,会将请求交给 POST 过滤器,最后返回给用户。
- 如果是 error 过滤器自己出现异常,最终也会进入 POST 过滤器,将最终结果返回给请求客户端。
- 如果是 POST 过滤器出现异常,会跳转到 error 过滤器,但是与 pre 和 route 不同的是,请求不会再到达 POST 过滤器了。
4. Gateway-server:
4.1 基础配置:
在 imooc-ad-spring-cloud 下,创建 module:ad-gateway。
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>imooc-ad</artifactId>
<groupId>com.imooc.ad</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<!-- 当前项目/模块的坐标, groupId从父模块中继承 -->
<artifactId>ad-gateway</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 模块名及描述信息 -->
<name>ad-gateway</name>
<description>ad-gateway</description>
<dependencies>
<!--
Eureka 客户端, 客户端向 Eureka Server 注册的时候会提供一系列的元数据信息, 例如: 主机, 端口, 健康检查url等
Eureka Server 接受每个客户端发送的心跳信息, 如果在某个配置的超时时间内未接收到心跳信息, 实例会被从注册列表中移除
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 服务网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
<!--
SpringBoot的Maven插件, 能够以Maven的方式为应用提供SpringBoot的支持,可以将
SpringBoot应用打包为可执行的jar或war文件, 然后以通常的方式运行SpringBoot应用
-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
启动类:
@EnableZuulProxy
@SpringCloudApplication
public class ZuulGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApplication.class,args);
}
}
application.yml:
server:
port: 9000
spring:
application:
name: ad-gateway
eureka:
client:
service-url:
defaultZone: http://server1:8000/eureka/
4.2 自定义网关过滤器:
接下来,实现网关中最核心的概念——过滤器,用于日志的操作。
过滤器1:PreRequestFilter
//将过滤器注册到容器中
@Slf4j
@Component
public class PreRequestFilter extends ZuulFilter {
//filter执行类型
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
//filter执行顺序
@Override
public int filterOrder() {
return 0;
}
//是否需要执行过滤器
@Override
public boolean shouldFilter() {
return true;
}
//具体操作,记录该操作的起始时间戳
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.set("startTime",System.currentTimeMillis());
return null;
}
}
过滤器2:AccessLogFilter
@Slf4j
@Component
public class AccessLogFilter extends ZuulFilter {
//filter执行类型
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
//filter执行顺序
@Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
}
//是否需要执行过滤器
@Override
public boolean shouldFilter() {
return true;
}
//具体操作:日志记录操作的执行时间和路径
@Override
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
long startTime = (Long) context.get("startTime");
//请求路径
String uri = request.getRequestURI();
//请求时间
long duration = System.currentTimeMillis() - startTime;
Logger log = null;
assert false;
log.info("uri: " + uri + ", duration: " + duration / 100 + "ms");
return null;
}
}
这样的话,整个广告系统的骨架就开发完成了。