1. 什么是我服务?
2. 微服务之间是如何独立通讯的?
3. springcloud和dubbo有哪些区别?
4. springboot和springcloud之前有神区别,谈谈对他们的理解。
5. 什么是微服务熔断?什么是微服务降级?
6. 微服务的优缺点分别是什么?说下你在项目开发中遇到的坑。
7. 你所知道的微服务技术栈有哪些?列举一二。
8. Eureka和Zookeeper都可以提供微服务注册与发现的功能,说说两者区别。
微服务
微服务架构
优点
缺点
微服务的技术栈有哪些?
springcloud官网:https://spring.io/
1.分布式+服务治理 Dubbo
目前成熟的互联网架构,应用服务化拆分 + 消息中间件
2.Dubbo 和 SpringCloud对比
可以看一下社区活跃度:
https://github.com/dubbo
https://github.com/spring-cloud
对比结果:
Dubbo | SpringCloud | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netfilx Hystrix |
服务网关 | 无 | Spring Cloud Netfilx Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总栈 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
最大区别:Spring Cloud 抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式
严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这个优点在当下强调快速演化的微服务环境下,显得更加合适。
我们会使用一个Dept部门模块做一个微服务通用案例Consumer消费者(Client)通过REST调用Provider提供者(Server)提供的服务。
回顾Spring,SpringMVC,Mybatis等以往学习的知识。
Maven的分包分模块架构复习。
一个简单的Maven模块结构是这样的:
一个工程带多个Moudule子模块。
MicroServiceCloud父工程(Project)下初次带着3个子模块(Module)
SpringBoot | SpringCloud | 关系 |
---|---|---|
1.2.x | Angel版本(天使) | 兼容SpringBoot1.2x |
1.3.x | Brixton版本(布里克斯顿) | 兼容SpringBoot1.3x,也兼容SpringBoot1.4x |
1.4.x | Camden版本(卡姆登) | 兼容SpringBoot1.4x,也兼容SpringBoot1.5x |
1.5.x | Dalston版本(多尔斯顿) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
1.5.x | Edgware版本(埃奇韦尔) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
2.0.x | Finchley版本(芬奇利) | 兼容SpringBoot2.0x,不兼容SpringBoot1.5x |
2.1.x | Greenwich版本(格林威治) |
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.haustgroupId>
<artifactId>springcloudartifactId>
<version>1.0-SNAPSHOTversion>
<modules>
<module>springcloud-apimodule>
<module>springcloud-provider-dept-8001module>
<module>springcloud-consumer-dept-80module>
<module>springcloud-eureka-7001module>
<module>springcloud-eureka-7002module>
<module>springcloud-eureka-7003module>
<module>springcloud-provider-dept-8002module>
<module>springcloud-provider-dept-8003module>
<module>springcloud-consumer-dept-feignmodule>
<module>springcloud-provider-dept-hystrix-8001module>
<module>springcloud-consumer-hystrix-dashboardmodule>
<module>springcloud-zuul-9527module>
<module>springcloud-config-server-3344module>
<module>springcloud-config-client-3355module>
<module>springcloud-config-eureka-7001module>
<module>springcloud-config-dept-8001module>
modules>
<packaging>pompackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<junit.version>4.12junit.version>
<log4j.version>1.2.17log4j.version>
<lombok.version>1.16.18lombok.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>0.2.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Greenwich.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.1.4.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
dependency>
dependencies>
dependencyManagement>
project>
父工程为springcloud,其下有多个子mudule,详情参考完整代码了解
springcloud-consumer-dept-80访问springcloud-provider-dept-8001下的controller使用REST方式
如DeptConsumerController.java
/**
* @Auther: csp1999
* @Date: 2020/05/17/22:44
* @Description:
*/
@RestController
public class DeptConsumerController {
/**
* 理解:消费者,不应该有service层~
* RestTemplate .... 供我们直接调用就可以了! 注册到Spring中
* (地址:url, 实体:Map ,Class responseType)
*
* 提供多种便捷访问远程http服务的方法,简单的Restful服务模板~
*/
@Autowired
private RestTemplate restTemplate;
/**
* 服务提供方地址前缀
*
* Ribbon:我们这里的地址,应该是一个变量,通过服务名来访问
*/
private static final String REST_URL_PREFIX = "http://localhost:8001";
//private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
/**
* 消费方添加部门信息
* @param dept
* @return
*/
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept) {
// postForObject(服务提供方地址(接口),参数实体,返回类型.class)
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
/**
* 消费方根据id查询部门信息
* @param id
* @return
*/
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
// getForObject(服务提供方地址(接口),返回类型.class)
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}
/**
* 消费方查询部门信息列表
* @return
*/
@RequestMapping("/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
使用RestTemplete先需要放入Spring容器中
ConfigBean.java
@Configuration
public class ConfigBean {
//@Configuration -- spring applicationContext.xml
//配置负载均衡实现RestTemplate
// IRule
// RoundRobinRule 轮询
// RandomRule 随机
// AvailabilityFilteringRule : 会先过滤掉,跳闸,访问故障的服务~,对剩下的进行轮询~
// RetryRule : 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行,重试
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
springcloud-provider-dept-8001的dao接口调用springcloud-api模块下的pojo,可使用在springcloud-provider-dept-8001的pom文件导入springcloud-api模块依赖的方式:
<dependency>
<groupId>com.haustgroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
springcloud-consumer-dept-80和springcloud-provider-dept-8001的pom.xml和父工程下的依赖基本一样,直接看完整代码即可,不再添加重复笔记。
Eureka基本的架构
Eureka包含两个组件:EurekaServer和EurekaClient.
Eureka server提供服务注册,各个节点启动后,回到EurekaServer中进行注册,这样eureka server中的服务注册表中将会存储所有可用服务节点信息,服务节点的信息可以在界面中直观的看到。
Eureka client 是一个Java客户端,用于简化Eureka server的交互,客户端同事也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServcer发送心跳,如果EurekaServer在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册中心把这个服务节点移除掉。(默认周期为90S)
三大角色
<!--导包~-->
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<!--导入Eureka Server依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #表示是否向eureka注册中心注册自己
fetch-registry: false # fetch-registry如果为false,则表示自己是注册中心
service-url: #监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
@SpringBootApplication
@EnableEurekaServer //EnableEurekaServer服务端的启动类,可以接受别人注册进来
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class,args);
}
}
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
<version>1.4.6.RELEASEversion>
dependency>
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springCloud-provider-dept #修改eureka上面的默认描述信息
@SpringBootApplication
@MapperScan(basePackages = {"com.cy.*.dao"})
@EnableEurekaClient //在服务启动后自动注册到eureka服务中
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
# info配置
info:
# 项目的名称
app.name: cy.springCloud
# 公司的名称
company.name: cy.springCloud.com
Ribbon是一个基于Http和tcp的客户端负载均衡工具,基于Netflix Ribbon实现的,它不像spring cloud 服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个Spring Cloud微服务中.包括Fegin提供的声明式服务调用也是基于Ribbon实现的.Ribbon默认提供很多种负载均衡算法,列入轮询,随机等。
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //负载均衡的实现方式
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
@FunctionalInterface
public interface ClientHttpRequestInterceptor {
ClientHttpResponse intercept(HttpRequest var1, byte[] var2, ClientHttpRequestExecution var3) throws IOException;
}
当我们点进execute方法中,会根据我们的serverID拿到loadBalancer对象,这里面包含了注册的服务,然后再通过getServer完成负载均衡筛选。
loadBalancer对象中注册的服务。
Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口就是一种规则:
Ribbon默认采用是懒加载,即在第一次访问时才会去创建LoadBalanceClient,请求时间会很长,而饥饿加载则会在项目启动时去创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
nacos是阿里巴巴的产品,现在是springcloud中的一个组件,想比eureka功能更加丰富,在国内受欢迎程度较高。
中文官网:https://nacos.io/zh-cn/
在GitHub中下载nacos安装包
1.4.4版本比较稳定,解压得到文件夹目录
在bin目录下,双击startup.cmd启动nacos
启动成功,http://192.168.3.160:8848/nacos/index.html这是nacos的地址
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.5.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
spring:
application:
name: springCloud-consumer #服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos服务
spring:
application:
name: springCloud-consumer #服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos服务
cluster-name: SH #集群名称 代指南京
nacos中的配置文件变更后,微服务无需重启就可以感知,不过需要通过下面两种配置实现。
Feign 是一个声明式的 REST 客户端,它能让 REST 调用更加简单。Feign 供了 HTTP 请求的模板,通过编写简单的接口和插入注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。官方地址:https://github.com/OpenFeign/feign
而 Feign 则会完全代理 HTTP 请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。
feign运行自定义配置来覆盖默认配置,可以修改的配置如下:
feign底层的客户端实现:
因此优化feign的性能主要包括:
方式一(继承):给消费者的FeignClient和提供者的Controller定义统一的父接口作为标准
方式二(抽取):将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用。
实现最佳实践方式二的步骤如下:
当定义的feignClient不在springbootApplication的扫描包范围内,这些feignClient无法使用。有两种方式解决
方式一: 指定feignClient所在包
方式二:指定feignClient字节码
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
在springcloud中网关的实现包括两种
搭建网关服务的步骤:
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
spring提供了31种不同的路由过滤器工厂,例如:
案例:给所有进入userservice的请求添加一个请求头Truth,123456
实现方式:在gateway中修改application.yml文件,给userservice的路由添加过滤器
全局过滤器的作用也是出力一切进入网关的请求和微服务响应,与gatewayFilter的作用一样,区别在于gatewayFilter通过配置定义,处理逻辑是固定的。而globalFilter的逻辑需要自己去写代码实现。
定义方式是实现GlobalFilter接口。
总结:
请求进入网关会碰到三类过滤器:当前路由过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器
跨域:域名不一致就是跨域,主要包括:
什么是MQ
MQ(MessageQueue),中文是消息队列,字面来看就是存放消息的队列,也就是事件驱动架构中的broker。
RabbitMQ中的几个概念:
常见消息模型
MQ的官方文档中给出了5个MQ的Demo示例,对应了几种不同的用法: