今天我们主要学习的内容包含5个部分,分别为认识微服务,分布式服务架构案例,euraka注册中心,Ribbon负载均衡原理,nacos注册中心。
目录
一、微服务
1.1、认识微服务
1.2、服务的拆分及远程调用
1.3、 euraka注册中心
1.4、Ribbon负载均衡原理
1.5、 nacos注册中心
1.6、Eureka和Nacos对比
首先我们看一下微服务的架构,首先看一下单体架构,一般适用于小型的项目,将所有业务的功能在一个项目中开发,然后进行打包放到服务器中,客户端就可以进行访问了,这种部署简单,但是代码耦合度较高,不适合大型项目的发布和维护。
对于大型的项目一般采用分布式架构,分布式架构就是根据功能拆分成不同的项目,每个功能对应一个项目,即对应一个服务,分布式架构有利于降低模块之间的耦合性,当然分布式结果也会带来一些问题。
我们可以看到微服务也面临下面几个问题,比如:怎么去拆分,拆分模块的力度怎么把握?对与服务需要集群,集群的地址怎么维护呢?由于服务都是在不同的项目下,如果需要调用别的服务的接口怎么调用呢?为了防止调用坏掉的服务,防止级联失败,怎么去感知服务是否健康呢?
目前常用的是分布式微服务架构,微服务架构特征如下:
包括:单一职责,面向服务,独立,隔离性强;本质上 就是为了实现高内聚和低耦合,降低服务之间的影响范围。
下面我们看一下常用的微服务技术的对比:
我们先了解一下SpringCloud微服务体系,具体如下:
SpringCloud是集成了各种微服务的组件,基于SpringBoot实现了这些组件的自动装配,使得实现了开箱即用的效果。
下面看一下SpringCloud和SpringBoot的版本兼容介绍,我们学习用的版本是boot版本的。
1)服务的拆分
在进行服务拆分之前,我们需要注意以下几个问题,首先,需要注意要保证不同的微服务开发不同的业务,防止业务的重复开发;微服务的数据应该独立,应该做到仅访问自己的数据库,不访问其它微服务的数据库;微服务应该可以将自己的业务暴露为接口,供其它微服务调用。
我们使用了一个已有的demo演示服务的拆分和远程调用,首先我们把订单查询和id查询拆分成两个不同的业务,两个数据表去模拟两个不同的数据库。
启动两个微服务,如下有两个运行的微服务,分别是根据id查用户和根据id查订单。
我们现在 浏览器测试一下两个微服务是否能正常工作,如下:
2) 服务的远程调用
根据正常的需求要求根据id查询订单同时返回订单消息和用户消息,因为属于不同的服务,那么就需要涉及服务的远程调用了。
一般可以使用注册RestTemplate实现服务的远程调用,首先在启动类创建RestTemplate对象,并注入Spring容器。
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@Bean //创建RestTemplate对象,并注入Spring容器
public RestTemplate restTemplate(){
return new RestTemplate() ;
}
}
然后就可以在表现层注入RestTemplate对象,并使用该对象发送http请求,实现服务器的远程调用,具体如下:
import cn.itcast.feign.pojo.User;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate ;
@GetMapping("{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
// 根据id查询订单并返回
Order order = orderService.queryOrderById(orderId) ;
// 利用restTemplate发送http请求,查询用户
String url = "http://localhost:8081/user/" + order.getUserId();
//发送http请求实现远程调用
User user = restTemplate.getForObject(url, User.class);
// 封装uer对象到order中
order.setUser(user) ;
return order;
}
}
测试结果如下:可以看到在查询用户订单信息,同时把用户信息也查询出来了。
我们在之前的案例中可以发现order-service是调用user-service,所有前者是服务的消费者,后者是服务的提供者。对于服务可以存在调用和调用的情况,即服务既是消费者又是提供者。
我们看一下Eureka的基本原理,服务提供者会向注册中心注册自己的信息,消费者需要信息时候,直接根据服务名称从注册中心拉去即可。如果服务提供者提供多个服务,一般会根据负载均衡原理从服务列表中挑一个。服务提供者每30s会向注册中心提供一次心跳,这样就可以感知服务的健康状态了,有效地避免了消费者拉去到不健康的服务。
接下来我们进行实践练习一下,该实践一共分为3步,具体如下,首先搭建注册中心,然后将服务都注册到注册中心,最后在order-service中完成服务拉取,通过负载均衡挑选一个服务实现远程调用。
主要分为三个步骤:1、引入依赖;2、加入启动注解;3、配置eureka地址。
引入依赖:
cloud-demo
cn.itcast.demo
1.0
4.0.0
eureka-server
8
8
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
在启动类加入启动注解:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
配置eureka地址:
server:
port: 10086 # 服务端口
spring:
application:
name: eurekaserver # eureka的服务名称
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
在浏览器测试效果如下,即说明搭建注册中心成功。
2)服务注册
服务注册一共两步:导入依赖、配置eureka地址。
将user-service和order-service分别进行注册,具体如下:
在pom.xml配置依赖如下:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
在.yml文件种配置地址,当然也可以把服务的名称加上。
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
在浏览器测试注册效果如下,可以发现服务均注册到eureka注册中心。
另外,可以再次启动user-service服务,模拟多个实例部署,方法如下:
部署完实例后,启动,可以在浏览器的注册中心看到新注册的实例,一个服务有两个实例,如下:
3)在order-service中完成服务拉取,通过负载均衡挑选一个服务实现远程调用
只需要将表现层的硬编码获取url方式,改成通过服务名称从注册中心拉取即可,然后需要加上负载 均衡注解。
import cn.itcast.feign.pojo.User;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate ;
@GetMapping("{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
// 根据id查询订单并返回
Order order = orderService.queryOrderById(orderId) ;
// 利用restTemplate发送http请求,查询用户
String url = "http://userservice/user/" + order.getUserId();
//发送http请求实现远程调用
User user = restTemplate.getForObject(url, User.class);
// 封装uer对象到order中
order.setUser(user) ;
return order;
}
}
在启动类的远程调用服务对象RestTemplate中加入负载均衡注解即可。
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
@LoadBalanced
@Bean //创建RestTemplate对象,并注入Spring容器
public RestTemplate restTemplate(){
return new RestTemplate() ;
}
}
总结:本节主要学习搭建注册中心,注册服务,发现服务(按照负载均衡从注册中心拉去服务)
1)负载均衡流程
我们先看一下负载均衡的流程,具体如下 ,首先是消费服务发送请求到Ribbon,Ribbon向注册中心拉去服务,然后通过负载均衡原理选取一个实例完成服务。
那么Ribbon中做负载均衡的具体流程是什么样?我们看一下,首先Ribbon客户端会使用拦截器拦截请求,交给Dynamic**对象去获取url中的id并从eureka注册中心中拉去服务列表,根据IRule中的负载均衡规则选择某个服务交给Ribbon客户端,由Ribbon客户端选择一个服务完成请求。
2)负载均衡策略
负载均衡的规则有很多,常见的规则如下所示,有轮循,随机等规则。
既然知道了有上述的规则,那么如何自定义选择实现具体的负载均衡策略呢,如下:
有两种方式,第一种,使用代码的方式,自定义负载均衡的规则,这种是全局方案。
第二种是针对某一个具体的微服务而言的,直接在配置文件中修改该服务的负载聚恒规则。
3)Ribbon饥饿加载
对于Ribbon的懒加载,第一次加载项目会耗时较大,一般可以配置成饥饿加载的方式。
总结:本节学习了Ribbon负载均衡的原理,主要包含三个方面,第一,负载均衡的规则,第二,负载均衡的定义方式,第三,设置饥饿加载。
1)认识和安装Nacos
首先Nacos注册中心是阿里的产品,是springcloud的一个组件,使用度还是比较高的,值得学习。
去官网下载即可,下载后解压,在bin目录下,使用命令行启动Nacos,如下:
然后可以通过给出的浏览器地址进行访问,初始的用户名和密码都是nacos,如下:
2)Nacos快速入门
对于Nacos注册中心的使用,首先需要配置主配置文件管理依赖。
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2.2.5.RELEASE
pom
import
然后修改原来的user-service中的eureka注册中心依赖换成Nacos注册中心依赖,如下:
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
然后需要在.yml文件中配置nacos的服务器地址,具体如下:
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
application:
name: userserver # user的服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置nacos服务器地址
mybatis:
type-aliases-package: cn.itcast.user.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
#eureka:
# client:
# service-url: # eureka的地址信息
# defaultZone: http://127.0.0.1:10086/eureka
将order-service注入到nacos中的方法和这个一样,不再赘述。
最后,启动两个服务,在注册中心nacos服务器的业面可以观察到注册的服务,user服务有2个实例,order服务有一个实例。
3)Nacos服务器分级存储模型
Nacos服务分级存储模型主要包含三级,服务包含多个集群,每个集群中包含若干个实例。
如果像设置服务的集群属性,我们呢首先创建三个user服务,然后需要在.yml文件中进行配置,如下属性,并启动服务,则该服务即属于该集群。
然后就可以在nacos的服务端看到三个实例分布在三个集群中,如下:
我们总结一下NacosRule负载均衡的策略,就是优先选择同一集群的实例,没有找到,才去采用负载均衡挑选实例。
另外,对于负载均衡问题,实际部署的场景中,有些服务器的性能好,一些差,根据服务器性能分配负载任务,即服务器性能好的分配的权重大一些。
直接在nacos服务端的控制台修改权重就可以了,权重越大,服务被访问的概率越大。
4)Nacos环境隔离
Nacos的服务和数据存储最外层都有一个名叫namespace的用来做外层隔离的东西。
在Nacos服务端建立一个命名空间,如下所示:
需要在.yml文件中配置命名空间,主要是根据上面命名空间生成的id进行配置。
然后重启order-service服务,可以在Nacos中发现该服务已经放到新的命名空间dev中,而不是在命名空间public中。
最后,需要注意的是namespace用来做环境隔离,每个namespace都有唯一的id,不同的namespace下服务不可见。
注册中心Eureka和Nacos都支持服务注册和周期性拉取,都支持用心跳方式做健康检测。
它们俩的区别在于Nacos支持注册检测提供者的健康状态,对于临时实例和非临时实例采用不同检测模式。两个注册中心各有优势。