Spring 框架学习 (五)Spring Cloud
Spring Cloud
基于自己的理解
是什么
一系列组件构成,这些组件用于帮助构建微服务架构的系统。
解决什么问题
微服务架构在增加了系统的可维护性,可用性等基础上,增加了系统开发的复杂度:
- 不同服务之间调用,需要知道每一个服务的地址
- 对比不使用微服务架构(即使用一整个单独、庞大的系统)时,只要内部调用接口就好了
- 服务调用时,使用HttpClient/RestTemplate写的代码稍显冗杂
- 对比单独系统,同样只要内部调用接口就好了
- 高可用部署时,需要负载均衡访问服务
- 单独系统同样不存在这个问题
- 外部调用系统接口时,需要知道系统每一个服务地址
- 单独系统只有一个地址,所以不存在这个问题
- 存在服务错误导致的雪崩问题,即如果一个服务挂掉不响应,则相应的上游业务会由于请求卡住,也全部挂掉。
- 单独系统倒不能说不存在这个问题,但是可能没有这个概念。
- 微服务数量上升,每个服务都有配置文件,不方便管理。尤其是可能有一些公共配置(比如数据库、redis),重复配置也比较麻烦
目前初步学习了一些Spring Cloud相关内容,里面的几个组件就是针对这些问题的。
怎么用
还是分组件来看
服务注册发现
这里包含一个注册服务(一个单独的进程/集群服务),和多个微服务。
每个微服务客户端向注册服务注册自己的地址,并且也可以从注册服务查询自己需要的微服务的地址。
在SpringBoot的AutoConfiguration
的支持下,注册一般只要添加好相应的依赖,然后配置一下注册服务的地址。客户端就可以自动以应用的名字注册。
使用时,在应用中通过@EnableDiscoveryClient
使能服务查询,通过注入的DiscoveryClient
去找到所需要的服务,然后发送请求。服务的查找一般通过服务的名称,或者id。
List<ServiceInstance> instances = discoveryClient.getInstances("service-name");
ServiceInstance serviceInstance = instances.get(0);
System.out.println(serviceInstance.getUri());//获取服务地址
这样各个客户端/微服务之间不需要写死其他服务的地址,而是通过名字去查询所需要的服务地址。
不同的注册服务
其实只试验了一下Eureka和Consul。
Eureka: 似乎已经停止维护了,可以通过SpringBoot启动一个Eureka服务。
Consul:是一个单独的二进制文件运行。可以进行健康检查等。
负载均衡
通过服务发现,如果一个服务注册了多个实例(高可用部署),那么会发现多个。
SpringCloud中包含LoadBalancerClient
类可以被自动注册,他可以帮助我们自动在多个里选择用于调用的服务。
ServiceInstance serviceInstance = loadBalancer.choose("service-name");
System.out.println(serviceInstance.getUri());
Feign
Feign
组件帮助简化调用其他服务代码的书写。
通过RestTemplate
:
public void doRequest() {
... //获取服务地址等
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = null;
try {
response = restTemplate.exchange(baseUrl,
HttpMethod.GET, getHeaders(), String.class);
} catch (Exception ex) {
System.out.println(ex);
}
System.out.println(response.getBody());
...
}
private static HttpEntity<?> getHeaders() throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
return new HttpEntity<Object>(headers);
}
通过Feign
:
//配置Feign
@FeignClient("service-name")
public interface RpcService {
@RequestMapping(method = RequestMethod.GET, value = "/doRequest")
public Object getData();
}
//使用
@Autowired
private RpcService rpcService;
public String doRequest() {
rpcService.getData();
}
这里@FeignClient
注解会自动查找服务地址,而且还会自动进行负载均衡选择。
使用Feign
时,应用配置类或者Main类需要添加@EnableFeignClients
。
服务熔断降级
熔断,就是当某个服务接口调用多次失败,下一次就不调用了。
而降级,就是熔断后所做的替代操作。
比如订单一直失败,这时候在下单,服务端就不去提交订单,而是直接显示,当前系统繁忙,请稍后再试。
熔断功能可以通过Hystrix
实现。
使用
应用配置类需要增加@EnableCircuitBreaker
注解开启熔断功能。
在需要熔断的服务上增加:
@HystrixCommand(fallbackMethod = "getDataFallBack")
这样会使用默认的配置进行熔断,比如10秒超过20次失败就会熔断,调用fallbackMethod处理。
内部上,使能服务熔断功能,会让服务内部创建一个线程池,用于处理发过来的请求,这样当请求响应不过来,线程池满了的话,也不会消耗其他系统资源。
微服务网关
网关作为微服务系统内部和外部之间的桥梁,外部系统不用知道内部微服务各个子服务的地址,也不用去注册发现服务去找地址(因为这对于一个外部应用来说仍显得麻烦),而是将所有请求发给微服务网关,由网关判断微服务的实际地址。
Zuul
Zuul
似乎是Spring官方推荐的一个微服务网关。
Zuul
本身也向配置中心注册,获取其他服务的中心,但是调用者只要调用Zuul
的地址,就可以通过Zuul
将请求转发给后端的实际服务。
Zuul
通过配置,来判断入口请求转发给哪个服务。如:
zuul:
routes:
employee:
path: /emp/**
service-id: employee-service
order:
path: /order/**
service-id: order-service
这里以emp
开头的请求就会分发给employee-service
服务,order
开头的请求就会发送给order-service
。
这里通过service-id
,Zuul
会自动实现相同service-id
的请求负载均衡。
配置中心
将配置放在一个公共服务里进行维护,这个服务就是配置中心。
Consul
除了作为注册发现服务,Consul提供了K/V存储功能,可以用于配置中心。
添加MAVEN依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
在bootstrap.yml
中配置注册中心:
spring:
application:
name: my-service
profiles:
active: dev
cloud:
consul:
host: localhost
config:
enabled: true
prefix: config
data-key: data
profileSeparator: '::'
format: yaml
watch:
delay: 30
按照上面的配置,启动时,默认会读取这几个key中的内容作为yaml
格式的配置加载(在上方的具有高优先级):
config/my-service::dev/data
config/my-service/data
config/application::dev/data
config/application/data
这里的key
看起来像是目录结构,其实/
符号同样是作为key的一部分的。