1、微服务构建:springboot
pom.xml 基本引入(也可通过springInitlizr 时选择导入)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这样可以配置spring的基本注解,并进行使用
启动:java -jar target/${project.build.finalname}.jar --spring.profiles.active=xxx
2、服务治理: Spring Cloud Eureka
2.1、server端
-
pom.xml
<!-- 当需要两个注册中心相互注册同步时引入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
-
主程序(EurekaApplication)
加注解@EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
-
配置文件 (peer1 peer2相互注册)
application.yml
spring:
profiles:
#默认使用peer
active: peer1
application:
name: eureka-server
eureka:
server:
# 自我保护模式,当出现出现网络分区、eureka在短时间内丢失过多客户端时,会进入自我保护模式,即一个服务长时间没有发送心跳,eureka也不会将其删除,默认为true
enable-self-preservation: false
# 过期实例应该启动并运行的时间间隔,单位为毫秒,默认为60 * 1000
eviction-interval-timer-in-ms: 60000
application-peer1.yml
server:
port: 1111
eureka:
instance:
hostname: peer1
client:
#eureka服务器上注册自己
register-with-eureka: false
#不获取eureka服务器注册表上的注册信息
fetch-registry: false
#注册中心位置-相互注册
service-url:
defaultZone : http://peer2:1111/eureka/
application-peer2.yml
server:
port: 2222
eureka:
instance:
hostname: peer2
client:
#eureka服务器上注册自己
register-with-eureka: false
#不获取eureka服务器注册表上的注册信息
fetch-registry: false
#注册中心位置-相互注册
service-url:
defaultZone : http://peer1:1111/eureka/
2.2 client端
-
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
-
主程序**Application.java(如需发现服务时加,只注册不需要 例如:api-gateway)
增加@EnableDiscoveryClient
-
配置文件
application.yml
spring:
application:
name: **-service
eureka:
client:
service-url:
defaultZone: http://peer1:1111/eureka/,http://peer2:2222/eureka/
这样,client就可以通过service在Eureka注册的应用名(service自己的spring.application.name)进行调用,不需关注他的端口号
3、分布式配置中心: Spring Cloud Config&&消息总线: Spring Cloud Bus
(注:通过配置中心刷新配置文件的链接为:http://host:port/actuator/bus-refresh ;其中host、port为config-server服务的)
3.1 config-server端
-
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- 将配置中心注册的注册中心,别的服务只需通过serviceId即可调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<!-- 利用消息总线MQ自动刷新配置文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
-
主程序Application.java
增加注解@EnableConfigServer
-
配置文件 application.yml
server:
port: 9999
spring:
application:
name: config-server
cloud:
config:
server:
git:
#配置git地址
uri: https://github.com/**/configfiles
#消息总线刷新(必须1、2)
bus:
enabled: true
trace:
enabled: true
rabbitmq:
host: 192.168.*.*
port: 5672
username: *
password: *
eureka:
client:
service-url:
defaultZone: http://peer1:1111/eureka/,http://peer2:2222/eureka/
#消息总线刷新(必须3)
management:
endpoints:
web:
exposure:
include: bus-refresh
3.2 config-client端
-
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
-
主程序Application.java
增加@EnableDiscoveryClient去发现config-server服务
待刷新类需加注解@RefreshScope
-
配置文件 application.yml
server:
port:9090
spring:
application:
name: config-client
cloud:
config:
discovery:
enabled: true
service-id: config-server
profile: dev
rabbitmq:
host: 192.168.*.*
port: 5672
username: **
password: **
eureka:
client:
service-url:
defaultZone: http://peer1:1111/eureka/,http://peer2:2222/eureka/
4、声明式服务调用: Spring Cloud Feign
4.1 新建普通maven项目 为服务调用提供统一的接口**API(供服务生产者实现,供服务消费者继承)
@RequestMapping("/testSerivce")
public interface TestSerivce {
@RequestMapping("getTest")
public String getTest();
}
4.2服务生产者springboot:
@RestController
public class TestImpController implements TestSerivce {
@Override
public String getTest() {
//方法实现
return "";
}
}
4.3 服务消费者
-
pom配置:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
-
主程序Application.java配置
增加@EnableFeignClients @EnableDiscoveryClient
-
调用
service继承
@FeignClient(value = "test-service")
public interface ATestService extends TestService {
}
controller使用:
@RestController
public class AController {
@Resource
private ATestService aTestService;
@RequestMapping("/test")
public String getTest() {
String s = aTestService.getTest();
return "原始数据:" + s;
}
}
5、 API网关服务: Spring Cloud Zuul
5.1 pom.xml引入
<!-- 配置API网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<!-- 注册到注册中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 配置从配置中心读取-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
5.2 主程序注解配置
增加@EnableZuulProxy
5.3 application.yml配置
-
bootstrap.yml配置:
使用config,所以首先配置bootstrap.yml去注册中心读取配置中心,从而去查找配置
server:
port: 5555
spring:
application:
name: api-gateway
cloud:
config:
discovery:
enabled: true
service-id: config-server
eureka:
client:
service-url:
defaultZone: http://peer1:1111/eureka/,http://peer2:2222/eureka/
-
application.yml配置:
配置请求路由
zuul:
routes:
a:
path: /a/**
serviceId: a
b:
path: /b/**
serviceId: b
c:
path: /c/**
serviceId: c
d:
path: /d/**
serviceId: d
5.4 路由过滤
filterType: 过滤器的类型, 它决定过滤器在请求的哪个生命周期中执行。这里
定义为pre, 代表会在请求被路由之前执行。
- 第一个阶段pre, 在这里它会被pre类型的过滤器进行处理, 该类型过滤器的主要目的是在进行请求路由之前做一些前置加工, 比如请求的校验等。
- 第二个阶段routing, 也就是之前说的路由请求转发阶段, 请求将会被routing类型过滤器处理。这里的具体处理内容就是将外部请求转发到具体服务实例上去的过程, 当服务实例将请求结果都返回之后, routing 阶段完成。
- 第三个阶段post。此时请求将会被post类型的过滤器处理,这些过滤器在处理的时候不仅可以获取到请求信息, 还能获取到服务实例的返回信息, 所以在post类型的过滤器中, 我们可以对处理结果进行一些加工或转换等内容。
- 另外, 还有一个特殊的阶段error, 该阶段只有在上述三个阶段中发生异常的时候才会触发,但是它的最后流向还是post类型的过滤器,因为它需要通过post过滤器将最终结果返回给请求客户端
filterOrder: 过滤器的执行顺序。当请求在一个阶段中存在多个过滤器时, 需
要根据该方法返回的值来依次执行。
shouldFilter: 判断该过滤器是否需要被执行。这里我们直接返回了true, 因
此该过滤器对所有请求都会生效。实际运用中我们可以利用该函数来指定过滤器的
有效范围。
//记得加注解,将过滤器注入
@Component
public class PreFilter extends ZuulFilter {
private Logger logger = Logger.getLogger(PreFilter1.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
//判断逻辑,return true时 执行run方法,否则不执行
return true;
}
@Override
public Object run() {
logger.info("This is a preFilter");
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
if (accessToken == null) {
logger.info("accessToken is Null");
System.out.println("accessToken is Null");
//根据版本不同,post阶段sendErrorFilter判断逻辑不同,可查看源码查看其判断逻辑 我使用的是1.4.15
// ctx.set("error.status_code",HttpServletResponse.SC_GONE);
// ctx.set("SEND_ERROR_FILTER_RAN",false);
// ctx.setSendZuulResponse(false);
// ctx.setResponseStatusCode(HttpServletResponse.SC_GONE);
}else{ System.out.println("accessToken is OK");
logger.info("accessToken is OK");
}
return null;
}
}
6、分布式服务跟踪: Spring Cloud Sleuth &&消息驱动的微服务: Spring CloudStream
6.1 创建两个springboot项目
trace1,trace2 均注册到注册中心,配置中心,其中trace1需要调用trace2的服务,记住trace1配置feign,这里不做赘述。
6.2 在trace1 、trace2的pom.xml增加sleuth引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
6.3 通过日志追踪,用的默认的org.slf4j.Logger
6.3.1 在相应调用的类中,声明一个静态成员变量,记录相应日志
private static Logger logger = LoggerFactory.getLogger(LoanPropertyController.class);
6.3.2 就会在相应的日志输出中出现下述日志:
INFO [trace-1, f410ab57afd5cl45, a9f2118fa2019684, false)
• 第一个值: trace-1, 它记录了应用的名称,也就是application.properties中中spring.application.name参数配置的属性。
• 第二个值: f410ab57afd5c145, Spring Cloud Sleuth生成的一个ID, 称为Trace ID,它用来标识一条请求链路。一条请求链路中包含一个TraceID, 多个SpanID。
• 第三个值: a9f2118fa2019684, Spring Cloud Sleuth生成的另外一个ID, 称为SpanI D, 它表示一个基本的工作单元, 比如发送一个HTTP请求。
• 第四个值: false, 表示是否要将该信息输出到Zipkin等服务中来收集和展示。
6.3.3 logstash日志分析
为了解决日志分散在不同服务器,以及查看日志工作量的问题引入logstash
- pom.xml引入
<!-- https://mvnrepository.com/artifact/net.logstash.logback/logstash-logback-encoder -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>5.1</version>
</dependency>
- 配置logback-spring.xml,将日志以logstash支持的json方式输出到文件,一遍logstash去分析
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<property name="APP_Name" value="propertyLoan"/>
<property name="LOG_HOME" value="logs"/>
<timestamp key="bySecond" datePattern="yyyy-MM-dd'T'HHmmss"/>
<contextName>${APP_Name}</contextName>
<!-- 日志输出到控制台-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 日志添加到文件-->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/log/${APP_Name}.%d{yyyy-MM-dd-HH}.log</FileNamePattern>
<maxHistory>7200</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 为logstash输出json格式的appender-->
<appender name="LOGSTASHOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/json/${APP_Name}.json</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/json/${APP_Name}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"severity": "%level",
"service" : "${springAppName:-} ",
"trace": "%X{X-B3-Traceid:-} ",
"span": "%X{X-B3-Spanid:-} ",
"exportable": "%X{X-Span-Export:-}",
"pid": "${PIO:-}",
"thread": "%thread",
"class": "%logger{40}",
"rest": "%message"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<!-- >=debug级别输出到控制台-->
<root level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="STDOUT"/>
<appender-ref ref="LOGSTASHOUT"/>
</root>
</configuration>
6.3.4 zipkin日志分析
为了获取访问延迟的时间,引入zipkin
- 搭建zipkin-server服务
pom.xml
<!-- https://mvnrepository.com/artifact/io.zipkin.java/zipkin-server -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.10.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.zipkin.java/zipkin-autoconfigure-ui -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>2.10.2</version>
</dependency>
主程序增加注解@EnableZipkinServer
application.yml
server:
port: 9411
spring:
application:
name: zipkin-server
#去除控制台异常
management:
metrics:
web:
server:
auto-time-requests: false
- zipkin客户端配置例如:trace1、trace2
pom.xml引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
application.yml
#默认即为如此,本地测试则不需要配置
spring.zipkin.base-url=http://localhost:9411
至此,基本框架搭建就完成了