1 单体 集群
1.1 单体架构系统
所谓的单体架构,就像我们平常开发的应用——SpringBoot,所有功能都放在一个war包或者jar包里面。
单体架构的好处:便于开发,部署(jar或者war),测试。
1.2 集群
多个SpringBoot,他们的功能是一样,系统具有横向扩展能力。
使用集群的理由:
-
一般单台服务器处理的并发量为250左右,并发处理能力有限。
-
一般单台服务器中的一点服务器故障,整个服务就不能访问了。
2 结合生活中例子(分布式+集群)
以开一家球鞋的网店为例。
2.1 单体架构系统
由于自己没有开网店的经验,所以请了一位大学生。这位大学生从进货,整理货,客服,寄货,扫地,拖地等等,全部都做。
2.2 集群
请了两位大学生,这叫集群。
2.3 分布式
大学生的工资太高,请不起两位。那就把爷爷请过来进货,整理货,寄货,奶奶请过来扫地,拖地。这样大学生只要做客服就好了。
2.4 低耦合
爷爷、奶奶、大学生互不影响对方的服务。
2.5 高内聚
大学生客服回复的快慢不影响爷爷奶奶的服务。
2.5 集群+分布式
姥姥奶奶一起轮流扫地拖地,其中有一位想去玩麻将了,其中一个提供服务即可。
3 SpringCloud介绍
SpringCloud是一套工具,帮助开发者搭建出这么一个 集群和分布式的框架。
接下来介绍SpringCloud常用组件。
3.1 Eureka 服务注册与发现
是一个注册中心,每个微服务都会向注册中心注册自己的地址和端口和状态等信息。
每个微服务都会定时从注册中心获取服务列表,同时汇报自己的运行情况。
当要调用其他服务时,就可以从自己获取的服务列表中获取实例地址进行调用。
注解:
-
@EnableEurekaServer注解来启用Euerka注册中心功能
-
@EnableDiscoveryClient注解表明是一个Eureka客户端
eureka:
client: #eureka客户端配置
register-with-eureka: true #是否将自己注册到eureka服务端上去
fetch-registry: true #是否获取eureka服务端上注册的服务列表
service-url:
defaultZone: http://localhost:8001/eureka/ # 指定注册中心地址
enabled: true # 启用eureka客户端
registry-fetch-interval-seconds: 30 #定义去eureka服务端获取服务列表的时间间隔
instance: #eureka客户端实例配置
lease-renewal-interval-in-seconds: 30 #定义服务多久去注册中心续约
lease-expiration-duration-in-seconds: 90 #定义服务多久不去续约认为服务失效
metadata-map:
zone: jiangsu #所在区域
hostname: localhost #服务主机名称
prefer-ip-address: false #是否优先使用ip来作为主机名
server: #eureka服务端配置
enable-self-preservation: false #关闭eureka服务端的保护机制
3.2 Ribbon 负载均衡的服务调用
很多服务会部署多个,RestTemplate来调用其他服务时,Ribbon可以很方便的实现负载均衡功能。
使用@LoadBalance给RestTemplate负载均衡的能力
@Configuration
public class RibbonConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
Ribbon负载均衡的策略
-
RandomRule:从提供服务的实例中以随机的方式;
-
RoundRobinRule:以线性轮询的方式,就是维护一个计数器,从提供服务的实例中按顺序选取,第一次选第一个,第二次选第二个,以此类推,到最后一个以后再从头来过;
-
RetryRule:在RoundRobinRule的基础上添加重试机制,即在指定的重试时间内,反复使用线性轮询策略来选择可用实例;
-
WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择;
-
BestAvailableRule:选择并发较小的实例;
-
AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例;
-
ZoneAwareLoadBalancer:采用双重过滤,同时过滤不是同一区域的实例和故障实例,选择并发较小的实例。
配置
ribbon:
ConnectTimeout: 1000 #服务请求连接超时时间(毫秒)
ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
OkToRetryOnAllOperations: true #对超时请求启用重试机制
MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数
MaxAutoRetries: 1 # 切换实例后重试最大次数
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法
3.3 Hystrix 服务容错保护
服务于服务之间通过远程调用的方式进行通信,一旦某个被调用的服务发生了故障,其依赖服务也会发生故障。
Hystrix具备服务降级、服务熔断等强大功能。
注解:
-
@EnableCircuitBreaker来开启Hystrix的断路器功能
服务降级:
@HystrixCommand(fallbackMethod = "getDefaultUser")
public CommonResult getUser(Long id) {
return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
}
public CommonResult getDefaultUser(@PathVariable Long id) {
User defaultUser = new User(-1L, "defaultUser", "123456");
return new CommonResult<>(defaultUser);
}
3.4 OpenFeign 声明式服务调用
使用Feign时,整合了Ribbon和Hystrix的功能。
注解:
-
@EnableFeignClients注解来启用Feign的客户端
value表示对user-service服务调用,设置服务降级处理类为UserFallbackService
@FeignClient(value = "user-service",fallback = UserFallbackService.class)
public interface UserService {
}
3.5 Zuul API网关服务
API网关为微服务架构中的服务提供了统一分访问入口。它具有请求路由、校验过滤等功能。
注解:
-
@EnableZuulProxy注解来启用Zuul的API网关功能
给服务配置路由:
zuul:
routes: #给服务配置路由
user-service:
path: /userService/**
feign-service:
path: /feignService/**
配置访问前缀:
zuul:
prefix: /proxy #给网关路由添加前缀
过滤器类型:
-
pre:在请求被路由到目标服务前执行,比如权限校验、打印日志等功能;
-
routing:在请求被路由到目标服务时执行,这是使用Apache HttpClient或Netflix Ribbon构建和发送原始HTTP请求的地方;
-
post:在请求被路由到目标服务后执行,比如给目标服务的响应添加头信息,收集统计数据等功能;
-
error:请求在其他阶段发生错误时执行。
自定义过滤器:
@Component
public class PreLogFilter extends ZuulFilter {
private Logger LOGGER = LoggerFactory.getLogger(this.getClass());
/**
* 过滤器类型,有pre、routing、post、error四种。
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器执行顺序,数值越小优先级越高。
*/
@Override
public int filterOrder() {
return 1;
}
/**
* 是否进行过滤,返回true会执行过滤。
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 自定义的过滤器逻辑,当shouldFilter()返回true时会执行。
*/
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String host = request.getRemoteHost();
String method = request.getMethod();
String uri = request.getRequestURI();
LOGGER.info("Remote host:{},method:{},uri:{}", host, method, uri);
return null;
}
}
学习来自:
-
https://how2j.cn/
-
http://www.macrozheng.com/