一. 导学
微服务是分布式架构的一种,就是把服务做拆分。传统单体架构代码容易耦合,大型互联网项目要拆分。把一个独立的项目成为服务,最后形成服务集群
,一个业务可能需要用到多个服务。
注册中心
(拉取或注册服务信息),用以记录服务的IP和端口等服务,服务前往注册中心寻找另外一个服务。
配置中心
(拉取配置信息),统一管理整个服务集群的配置文件,实现配置热更新。
服务网关
负责对用户身份做校验,并作为请求路由,进行负载均衡。
由于数据库较少,用户数量庞大,还要进行分布式缓存
和分布式搜索
。
使用消息队列
进行异步通信,可以提高并发性能。
学习路线按类可以分为五部分,分别是微服务治理、缓存技术、搜索技术、异步通信技术和DevOps。
二. 认识微服务
单体架构:将业务所有功能集中在一个项目中开发,打成一个包部署。优点是架构简单,部署成本低。缺点是耦合度较高。
分布式架构:根据业务功能对系统进行拆分,每个业务模块独立开发,称为一个服务。优点是耦合度降低,利于服务升级拓展。但是要考虑的问题也会增多:
- 服务拆分粒度?
- 服务集群地址如何维护?
- 服务之间如何实现远程调用?
- 服务健康状态如何感知?
微服务是一种经过良好架构设计的分布式架构方案,其特征为(高内聚、低耦合):
- 单一职责:服务拆分粒度更小,每个服务对应一个业务能力,避免重复开发
- 面向服务:对外暴露业务接口
- 自治:团队独立、技术独立、数据独立、部署独立
- 隔离性强:隔离、容错、降级,避免出现级联问题
微服务方案需要技术框架落地,知名的框架包括SpringCloud和阿里巴巴Dubbo。
SpringCloudAlibaba提供Dubbo+SpringCloud的接口规范支持。
远程调用
我们希望通过发起HTTP请求调用另一个服务的RestFul Api接口。
-
注册RestTemplate
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}// 注册@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}
-
在业务中进行远程调用
@Service public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;public Order queryOrderById(Long orderId) {// 1.查询订单Order order = orderMapper.findById(orderId);// 2.利用RestTemplate发起HTTP请求,查询用户String url = "http://localhost:8081/user/" + order.getUserId();User user = restTemplate.getForObject(url, User.class);// 3.封装user到orderorder.setUser(user);// 4.返回return order;} }
三. Eureka注册中心
提供者与消费者
服务提供者:被其他微服务调用的服务
服务消费者:调用其他微服务的服务
一个服务既可以是提供者,也可以是消费者。
总体流程
Eureka搭建
第一步:引入依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
第二步:启动类加注解 @EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {public static void main(String[] args) {SpringApplication.run(EurekaApplication.class, args);}
}
第三步:配置文件application.yml
server:port: 10086 # 服务端口
spring:application:name: eurekaserver # 服务名称
eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka # eureka地址信息
Eureka服务注册
与上述过程类似,引依赖、配地址和服务名称,要注意依赖选择client,如下所示:
XML
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在IDEA中,对已运行的服务右键->Copy Configuration,在VM options中指定新的端口号,例如-Dserver.port=8082
,可以将原来的服务复制一份,部署后查看Eureka的注册中心情况。
服务发现
在前面的例子中,orderservice要调用userservice服务,因此我们在orderservice中引入eureka-client的依赖,并且修改请求URL:
String url = "http://userservice/user/" + order.getUserId();
然后在启动类中RestTemplate上加@LoadBalanced注解实现负载均衡。
Spring会自动根据服务名为userservice的实例列表,进行负载均衡并请求。
四. Ribbon负载均衡
过程
找到LoadBalancerInterceptor.class,跟踪代码,可以找到RibbonLoadBalancerClient.class。
可以看到,里面是先拿到allServerList,里面包含了被调用微服务userservice的服务列表,包括IP地址和端口号。
继续跟踪代码,会发现后续之行了一个名为chooseServer的函数,里面调用了Irule接口的实例。
在IDEA中,光标选中Irule接口,然后按快捷键Ctrl+H
,就可以看到它的实现。里面的规则包括RoundRobin(轮询)和Random(随机)等。在这次跟踪中,最后发现其采用的是ZoneAvoidanceRule。
总体流程如下图:
规则
上面说到有不同的rule(规则),不同规则的含义如下所示。
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
自定义规则
方案一(全局):在启动类或配置类中注入Irule即可,例如:
@Bean
public IRule randomRule(){return new RandomRule(); // 将负载均衡设置为随机
}
方案二(局部):在orderservice的application.yml中配置:
YML
userservice: # 针对某个微服务配置负载均衡规则,这里是userservice服务ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 规则为随机
饥饿加载
Ribbon默认懒加载,即第一次访问才去创建LoadBalanceClient。开启饥饿加载,可以在项目启动时就创建,降低第一次的访问耗时。
在上面的例子中,不开启饥饿加载的情况下,第一次访问加载大约要500ms。
开启后,响应时间降到250ms左右,快了一半,但仍比第二次访问的30ms慢很多,这是因为第一次还要加载DispatcherServlet等。
开启饥饿加载需要配置application.yml:
YML
ribbon:eager-load:enabled: trueclients: - userservice