SpringCloud

微服务基本架构图

服务注册 spring Cloud eureka

他是一个服务注册中心,服务消费者会注册到服务中心,服务实例会通过心跳方式定时通知服务中心(定时向注册中心更新实例。默认30S),Eureka Server收到心跳后,会通知集群里的其它Eureka Server更新此实例的状态。

  • 在Service Provider服务shutdown的时候,主动通知Eureka Server把自己剔除,从而避免客户端调用已经下线的服务。

  • Eureka Server会定时,进行检查,如果发现实例在在一定时间,内没有收到心跳,则会注销此实例。(间隔值是eureka.server.eviction-interval-timer-in-ms,默认值为60)

  • 保护模式:则会触发自我保护模式,此时Eureka Server此时会认为这是网络问题,它不会注销任何过期的实例。

其他服务注册中心也有Consul和Zookeeper

依赖

1
2
3
4
5
6
7
8
9
10
<!-- eureka server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

配置文件

server

1
2
3
4
5
6
7
8
9
10
11
12
13
server.port=9990
#服务名称
spring.application.name=eureka-server
#服务地址
eureka.instance.hostname=127.0.0.1
#是否注册eureka(当高可用模式下才会启用) 是否向服务注册中心注册自己
eureka.client.register-with-eureka=false
#是否启用获取服务注册信息
eureka.client.fetch-registry=false
#注册和查询都需依赖该地址,多个以逗号分隔
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
#留存的服务实例低于多少比列进入保护模式 默认是85%
eureka.server.renewal-percent-threshold=0.5

client

1
2
3
4
5
6
7
8
server.port=8083
spring.application.name=comment
#eureka server地址
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:9990/eureka/
# 心跳间隔 5s 默认30s
eureka.instance.lease-renewal-interval-in-seconds=5
# 服务失效时间: 如果多久没有收到请求,则可以删除服务 默认90s
eureka.instance.lease-expiration-duration-in-seconds=10

代码

eureka server在启动类添加注解@EnableEurekaServer
eureka client在启动类添加注解@EnableEurekaClient

@EnableEurekaClient和@EnableEurekaClient功能类似,但是@EnableEurekaClient只用于eureka。

网关服务 apigateway

我这里的例子是web服务,所有采用的是springBoot作为网关。如果是应用服务可以用spring Cloud zuul

所有的接口都是通过网关提供出去的,所以身份认证、路由服务、流量控制、日志统计代码编写都是在网关服务。

springBoot

配置

1
2
3
4
5
6
server.port=8090
spring.application.name=api-gateway
###########eureka配置#################
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:9990/eureka/
#是否向服务注册中心注册自己
#eureka.client.register-with-eureka=false

spring Cloud zuul

zuul主要就是路由转发和过滤器

依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>

配置

1
2
3
4
5
6
7
8
server.port=8769
spring.application.name=service-zuul
eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.path.serviceId=service-a
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.path.serviceId=service-b
#api-a 和api-b 都是对应子服务的名称

启动

启动类添加@EnableZuulProxy注解

网关与子服务通信

两种通信方式:restTemplate+httpclient和feign

restTemplate+httpclient

1、注入restTemplate

1
2
3
4
5
@Bean
//@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}

2、调用原子服务

post

1
int result = restTemplate.postForObject("http://comment/insert", comment, Integer.class);

get

1
2
3
Map<String, Integer> map = new HashMap<>();
map.put("nid", nid);
List<CommentUser> response = restTemplate.getForObject("http://comment/list?nid={nid}", List.class,map);

其中comment就是服务配置文件中的spring.application.name=comment

feign

依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

请求方式

类似于controller编写

1
2
3
4
5
6
7
8
9
@FeignClient(value = "comment")
public interface NewsFeign {

@RequestMapping(value="insert",method=RequestMethod.POST)
public int insert(@RequestBody Comment comment);

@RequestMapping(value="list",method=RequestMethod.GET)
public List<CommentUser> queryAll(int nid);
}

负载均衡ribbon

restTemplate+httpclient方式

1、需要添加依赖
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
2、添加注解@LoadBalanced
1
2
3
4
5
@Bean
@LoadBalanced // 启动负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}

feign

feign默认集成了ribbon

断路器hystrix

服务隔离(每个服务有单独线程池)、熔断(也可以称为断路)、降级等手段控制依赖服务的延迟与失败。
防止单个服务的故障,从而影响整个系统的性能,避免分布式环境里大量级联失败。使用快速失败策略,当调用一个服务超时,或者,线程池满了的情况,会立即拒绝服务而不会排队等待,还有服务降级,当调用失败的时候,就调用备用的服务。

依赖

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
###########hystrix配置################
#执行超时时间,超过这个时间执行没反应就返回
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000
#并发执行的最大线程数,默认10
hystrix.threadpool.default.coreSize=5
#线程最大队列数 默认1
hystrix.threadpool.default.maxQueueSize=1
#最大的线程池
#hystrix.threadpool.default.maximumSize=10
#错误比率阀值,如果错误率>=该值,断路器会被打开,并短路所有请求触发fallback。默认50
hystrix.command.default.circuitBreaker.errorThresholdPercentage=1
#触发短路的时间值,当该值设为5000时,则当触发circuit break后的5000毫秒内都会拒绝request,也就是5000毫秒后才会关闭circuit。默认5000
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=100000

代码

可以使用注解自定义配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Repository
@DefaultProperties(groupKey="newsDao",
commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000")},
threadPoolProperties={@HystrixProperty(name="coreSize",value="10")
,@HystrixProperty(name="maxQueueSize",value="1000")},
threadPoolKey="newsDao"
)
public class NewsDao {

@Autowired
private RestTemplate restTemplate;

public int insert(News news){
return restTemplate.postForObject("http://news/news/insert", news, Integer.class);
}

@HystrixCommand
public UserNews queryOne(int id){
Map<String,Integer> map = new HashMap<>();
map.put("id", id);
UserNews response = restTemplate.getForObject("http://news/news/query?id={id}", UserNews.class, map);
return response;
}

@HystrixCommand(fallbackMethod = "errFallback")
public List<News> queryAll(){
List<News> response = restTemplate.getForObject("http://news/news/list", List.class);
return response;
}
/**
* 有降级方法,但是用户线程池用完了的话,还是会启动短路器。并且一定的时间段调用还是会拒绝所有请求,当这个请求时间过了的时候,等下一次请求正常访问,断路器才会关闭
* @return
*/
public List<News> errFallback(){
return null;
}
}

启动方式

在启动类添加注解@EnableCircuitBreaker

服务跟踪系统Sleuth

分布式服务跟踪系统,微服务中,分布式服务架构,业务的调用链越来越复杂。分布式服务跟踪是整个分布式系统中跟踪一个用户请求的过程(包括数据采集、数据传输、数据存储、数据分析、数据可视化)提供链路追踪,故障快速定位,可视化各个阶段耗时,进行性能分析。理清服务依赖关系

依赖

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

配置文件

1
2
3
4
5
#############zipkin配置###############
#1就是 100%采样
spring.sleuth.sampler.percentage=1
#zipkin服务地址
spring.zipkin.baseUrl=http://127.0.0.1:9993

通过引入spring-cloud-starter-zipkin依赖和设置spring.zipkin.base-url就可以了。

监控系统

1、admin

健康检测,监控服务的堆栈,线程、GC、请求等信息

a. 创建一个admin服务,添加依赖。设置端口9992

1
2
3
4
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

b. 添加启动类注解@EnableAdminServer
c. 子服务添加依赖和admin服务地址配置

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>1.5.1</version>
</dependency>

1
2
3
4
5
6
###########spring-admin配置###########
spring.boot.admin.url=http://localhost:9992
#管理地址
management.port=8099
#关闭安全访问
management.security.enabled=false

2、hystrix dashboard

监控信息包括请求成功,失败(客户端抛出的异常),超时和线程拒绝。如果访问依赖服务的错误百分比超过阈值,断路器会跳闸,此时服务会在一段时间内停止对特定服务的所有请求

a. 创建hystrix-dashboard服务,添加依赖,端口为9991

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>

b. 启动类添加注解@EnableHystrixDashboard

3、zipkin

服务调用在整个链路中的状态可视化界面。sleuth收集数据,然后发送到zipkin存储展示。(默认存储到内存中,也可以存储到redis/mysql)

a. 创建zipkin服务,添加依赖,端口为9993

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>

b. 添加启动依赖注解@EnableZipkinServer

本文springCloud源码链接

ELK日志分析平台

服务产生的日志文件后,logstash去收集,然后解析,输出到ElasticSearch中,最后由kibana展示出来。

ElasticSearch

ElasticSearch是一个基于 Lucene 的搜索服务器。
安装:Elasticsearch 基础入门
常见坑:elasticsearch启动常见错误

kibana

数据可视化平台,安装最简单,安装完后指向ES地址即可。

logstash

Logstash 基础入门
Logstash 是一个开源的数据收集引擎,它具有备实时数据传输能力。
难点就是编写配置文件中的过滤器部分:grok 插件

grok 插件用于过滤杂乱的内容,将其结构化,增加可读性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
input{
file{
path => "C:\gaojindeng\mooc\api-gateway\logs\house-info*"
sincedb_path => "C:\gaojindeng\logstash-6.2.3\sincedb_house"
codec => multiline {
pattern => "^%{TIMESTAMP_ISO8601}"
negate => true
what => "previous"
}
}
}

filter {

}

output {
elasticsearch {
hosts => ["192.168.1.103:9200"]
index => "house-info-%{+YYYY.MM.dd }"
}
stdout {codec => rubydebug}
}

ELK整体搭建:搭建ELK日志分析平台

其他

SpringCloud Config:全局配置,比如git,svn,githup
SpringCloud Bus: +RabbitMQ 可以实现配置更新通知所有服务

log4j2 和logback日志对比

  • 异步模式性能优秀
  • 插件化架构易扩展

log4j2:多个线程会把日志放入到一个无锁化环形队列,然后由一个异步队列去拉取队列的数据打印到日志里面去

lightquant wechat
欢迎您订阅灯塔量化公众号!