目录
- 前情
- 微服务解决方案
- 常见面试题
- 微服务优缺点
- 微服务技术栈
- 深入全面学习spring cloud的网站
- Rest
- 父依赖
- 1.springcloud-api
- 2.提供者 springcloud-provider-dept-8001
- 3.消费者 springcloud-consumer-dept-80
- Eureka
- Eureka注册中心(Server)
- Eureka provider注册到Server(client)
- 服务发现
- Eureka集群配置
- CAP原则
- Ribbon
- ribbon测试
- ribbon负载均衡
- 自定义负载均衡算法
- Feign负载均衡
- Hystrix
- 服务熔断
- 服务降级
- Dashboard 流监控
- Zuul:路由网关
- 路径配置说明
前情
狂神springCloud 注意依赖版本的兼容问题
微服务架构4个核心问题?
- 服务很多, 客户端该怎么访问?
- 这么多服务? 服务之间如何通信?
- 这么多服务? 如何治理?
- 服务挂了怎么办?
微服务解决方案
常见面试题
微服务优缺点
微服务技术栈
深入全面学习spring cloud的网站
https://springcloud.cc/spring-cloud-netflix.html 中文API文档: https://springcloud.cc/spring-cloud-dalston.html SpringCloud中国社区: http://springcloud.cn/ SpringCloud中文网: https://www.springcloud.cc/
Rest
父依赖
<!--打包方式 -->
<packaging>pom</packaging>
<!--版本等配置-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.13.1</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.18.20</lombok.version>
<logback-core.version>1.2.3</logback-core.version>
</properties>
<!--管理模块中的jar包-->
<dependencyManagement>
<dependencies>
<!--springcloud依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springboot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.7.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback-core.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
1.springcloud-api
- pom
<!--父依赖若是有版本号则使用父依赖中的-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
- pojo dept类
@Data
@NoArgsConstructor
@Accessors(chain = true) //链式编程
public class dept implements Serializable {
//序列化,对象传输
private int did;
private String deptName;
private String dbName;
public void dept(String deptName){
this.dbName=deptName;
}
}
2.提供者 springcloud-provider-dept-8001
- pom
<dependencies>
<!-- 引入pojo类表关系映射 api module -->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- application.xml
server:
port: 8001
#mybatis配置
mybatis:
type-aliases-package: com.lzk.springcloud.pojo
mapper-locations: classpath:mapper/*.xml
#spring的配置
spring:
application:
name: springcloud-provider-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springclouddemo?useUnicode=true&characterEncoding=utf-8
username: root
password: xxxx
- dao service controller(和springboot一致,具体代码就不粘贴了)
- 启动访问
3.消费者 springcloud-consumer-dept-80
- pom
<!--要导入的包:实体类(api模块)+web-->
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- application.yml
server:
port: 80
- RestTemplateConf 配置Bean(生成Bean对象)
RestTemplate提供访问api并封装结果的spring对象
@Configuration
public class RestTemplateConf {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- deptController
消费者中访问的是生产者提供的接口
消费者暴露的接口才是面向客户端的
@RestController
public class deptController {
// 理解:消费者,不应该有service层。
// RestTemplate .... 供我们直接调用就可以了!
// 注册到Spring中
// (urL, 实体:Map , CLass<T> responseType)
@Autowired
private RestTemplate restTemplate;//提供多种便捷访问远程http服务的方法,简单的Restful服务模板
private static final String REST_URL_PREFIX = "http://localhost:8001/";
@RequestMapping("/dept/getAll")
public List<dept>getAllDept(){
//getForObject就是get请求,postForObject是post请求
return restTemplate.getForObject(REST_URL_PREFIX+"getAll",List.class);
}
}
- 启动
接下来的使用都是几个基本步骤
- 导入依赖(pom)
- 编写配置文件(yml)
- 开始某一功能 @EnableXXX (启动类上)
- 配置类(Configuration)
Eureka
Eureka注册中心(Server)
- 导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>
- 编写配置文件 application.yml
server:
port: 7001
spring:
application:
name: xxx # 微服务名
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否向注册中心注册自己
fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
service-url: # 监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- 开始某一功能 @EnableXXX (启动类)
@EnableEurekaServer // 开启服务
@SpringBootApplication
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class,args);
}
}
Eureka provider注册到Server(client)
- 导包
<!-- spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--actuator 提供完善的信息在后台-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置文件
#spring的配置
spring:
application:
name: springcloud-provider-dept # 微服务服务名
# eureka
eureka:
client:
# provider 配置将服务注册到哪个注册中心
service-url:
defaultZone: http://localhost:7001/eureka/
# 注册到eureka中心时的id,若不配置则默认描述
instance:
instance-id: springcloud-provider-dept-8001
# info配置,完善提供服务的信息(向注册中心提供该模块的一些信息,如模块名、编写的人)
info:
moudle.name: provider-dept
author.name: lzk
- 开启服务
@SpringBootApplication
@EnableEurekaClient //开启eureka客户端
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
左侧的【application是在左侧的spring.application.name配置,status下的是instance-id,点击可获得配置的info配置的详细信息
服务发现
查看注册中心的微服务的信息
- controller
//获取注册中心的服务的信息
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/discovery")
public Object discovery(){
//获取微服务列表清单
List<String> services = discoveryClient.getServices();
System.out.println("微服务:"+services);
//获取一个具体的微服务信息,通过服务名applicationName
List<ServiceInstance> instances=discoveryClient.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
for (ServiceInstance instance : instances) {
System.out.println(
instance.getHost()+'\t'+
instance.getPort()+'\t'+
instance.getUri()+'\t'
);
}
return discoveryClient;
}
- 开启服务
@SpringBootApplication
@EnableEurekaClient //开启eureka客户端
@EnableDiscoveryClient //开启服务发现
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
Eureka集群配置
基本和设置eureka注册中心无异=
将server_url 注册到其他集群上(不注册到自己的端口号下)
如端口7001eureka1.com、eureka2.com、eureka3.com在本地的host文件中已经有映射到
localhost
上
即是eureka1.com=eureka2.com=eureka3.com=localhost
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: eureka1.com #Eureka服务端的实例名称
client:
register-with-eureka: false # 表示是否向注册中心注册自己
fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
service-url: # 监控页面,并向其他注册中心注册自己
defaultZone: http://eureka2.com:7002/eureka/,http://eureka3.com:7003/eureka/
提供者
注册到所有集群中
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
CAP原则
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
Ribbon
- 流程(和下面的测试无关)
2. 策略
ribbon测试
这里说的都是消费者层的代码,因为负载均衡是在访问时进行的
- 导包
//eureka
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
//ribbon
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
- 配置
server:
port: 80
eureka:
client:
register-with-eureka: false # 不注册自己
service-url:
defaultZone: http://eureka1.com:7001/eureka/,http://eureka2.com:7002/eureka/,http://eureka3.com:7003/eureka/
- 声明Ribbon控制RestTemplate
@Configuration
public class RestTemplateConf {
@Bean
@LoadBalanced //ribbon控制客户端
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- 根据微服务名获取服务
至此无需关心ip和端口号
因为此时不再向http://ip:端口号
访问,而是通过服务名SPRINGCLOUD-PROVIDER-DEPT
(服务向注册中心注册的服务名)来访问
@RestController
public class deptController {
@Autowired
private RestTemplate restTemplate;
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT/";
@RequestMapping("/dept/getAll")
public List<dept>getAllDept(){
return restTemplate.getForObject(REST_URL_PREFIX+"getAll",List.class);
}
}
ribbon负载均衡
搭建如下项目环境,一个微服务(三个实例,不粘贴代码),根据负载均衡算法看访问哪个
负载均衡默认为轮询算法(使用同一个服务有多个实例的分配)
自定义负载均衡算法
import com.netflix.loadbalancer.IRule;
上面IRule获得微服务的状态,它的实现类控制负载均衡的算法实现
RoundRobinRuLe 轮询
RandomRule 随机
AvaiLabiLityFiLteringRuLe : 会先过滤掉,跳闸,访问故障的服务,对剩下的进行轮询
RetryRule : 会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行,重试
- 创建一个文件夹封装我们自己的均衡算法,该文件夹不能和启动类同一级
- 编写自己的均衡算法
public class IRule {
//使用自己定义的算法 myRandomRule:负载均衡实现类
@Bean
public myRandomRule getIRule(){
return new myRandomRule();
}
}
实现类
public class myRandomRule extends AbstractLoadBalancerRule {
public myRandomRule() {
}
/**
改造成:随机选取一个服务,执行5次之后再转换下一个服务
*/
//当前执行的服务次数
private int runNum=0;
//当前执行的服务
private int currNum=0;
@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;
while(server == null) {
if (Thread.interrupted()) {
return null;
}
//获取可达的服务
List<Server> upList = lb.getReachableServers();
//获取所有服务
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
if (runNum<5){
runNum++;
server = upList.get(currNum);
}else{
runNum=0;
//随机选取下一个服务索引
currNum = this.chooseRandomInt(serverCount);
server = upList.get(currNum);
}
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
- 开启指定服务使用均衡算法
@SpringBootApplication
//在微服务启动的时候就能去加载我们自定义Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT", configuration = IRule.class)
public class deptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(deptConsumer_80.class,args);
}
}
Feign负载均衡
feign替代restTemplate的功能,更加简洁,但是性能变低了,多了一层
对象:消费者
- 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
- api模块下
// 和@Mapper相似
@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEP")//微服务名applicationName
public interface deptService {
@RequestMapping("/getAll")
public List<dept> getAll();
}
- controller
@RestController
public class deptController {
@Autowired
private deptService service=null;//引入写好的类
@RequestMapping("/dept/getAll")
public List<dept>getAllDept(){
return service.getAll();
}
}
- 配置
server:
port: 80
eureka:
client:
register-with-eureka: false # 不注册自己
service-url:
defaultZone: http://eureka1.com:7001/eureka/,http://eureka2.com:7002/eureka/,http://eureka3.com:7003/eureka/
- 开启服务
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.lzk.springcloud"})
public class feignConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(feignConsumer_80.class,args);
}
}
feign整合了ribbon的负载均衡,所以还是可以将请求根据均衡算法分配
Hystrix
服务熔断
对象:提供者
复制一份springcloud-provider-dept-8001
- 导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
- controller
@RestController
public class deptController {
@Autowired
private deptService deptService;
@RequestMapping("/getAll")
@HystrixCommand(fallbackMethod = "demo")
public List<dept> getAll(){
if (true){
throw new RuntimeException("人为抛出错误。。");
}
return deptService.getAll();
}
//熔断后的备用方案
public List<dept> demo(){
dept d=new dept();
d.setDid(0).setDbName("未知").setDeptName("Hystrix测试");
List<dept> list=new ArrayList<>();
list.add(d);
return list;
}
}
- 开启服务
@SpringBootApplication
@EnableEurekaClient //开启eureka客户端
@EnableCircuitBreaker //添加熔断支持
public class DeptProvider_hystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_hystrix_8001.class,args);
}
}
服务降级
对象客户端
很多人访问一个微服务,将其他服务的请求不处理,返回提示,将资源给急需的服务使用
- 服务降级的回调工厂(调用类)
//服务降级 回调工厂
public class DeptClientServiceFallbackFactory implements FallbackFactory {
@Override
public deptService create(Throwable throwable) {
return new deptService() {
@Override
public List<dept> getAll() {
List<dept>list=new ArrayList<>();
dept d=new dept();
d.setDid(0).setDbName("未知").setDeptName("服务降级");
list.add(d);
return list;
}
};
}
}
- 设置服务降级的调用类
@Component
//设置降级调用的类
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface deptService {
@RequestMapping("/getAll")
public List<dept> getAll();
}
- 开启服务降级(消费者,该消费者)
# 开启降级
feign:
hystrix:
enabled: true
关掉提供者,查询消费者springcloud-consumer-feign-80
Dashboard 流监控
- 导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
- 配置application.yml
server:
port: 9001
hystrix:
dashboard:
proxy-stream-allow-list: "*"
- 开启服务
@SpringBootApplication
@EnableHystrixDashboard //开启监控页面
public class HystrixDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboard_9001.class,args);
}
}
此时监控页面可以启动服务
- 提供者要有监控信息依赖
<!--actuator 提供完善的信息再后台-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
- 提供者添加servlet Bean
@SpringBootApplication
@EnableEurekaClient //开启eureka客户端
@EnableDiscoveryClient //开启服务发现
@EnableCircuitBreaker //添加熔断支持
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
//增加一个Servlet
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean=new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
- 提供者controller中的接口要加上 @HystrixCommand
@HystrixCommand
@RequestMapping("/getAll")
public List<dept> getAll(){
return deptService.getAll();
}
- 开启注册中心、提供者,流量监控页面
监控方式:
Single Hystrix App: https://hystrix-app:port/actuator/hystrix.stream
eg: http://localhost:8001/actuator/hystrix.stream
Zuul:路由网关
- pom
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- application.yml
server:
port: 9527
#spring的配置
spring:
application:
name: springcloud-zuul
eureka:
client:
service-url:
defaultZone: http://eureka1.com:7001/eureka/,http://eureka2.com:7002/eureka/,http://eureka3.com:7003/eureka/
# 注册到eureka中心时的id,若不配置则默认描述
instance:
instance-id: springcloud-zuul
zuul:
routes:
dept.serviceId: springcloud-provider-dept # 微服务名
dept.path: /mydept/**
ignored-services: "*" # 不能使用这个路径访问, * :不能使用所有微服务名访问
prefix: /lzk # 所有服务的公共前缀
- 启动类
@SpringBootApplication
@EnableZuulProxy //开启路由代理
public class Zuul_9527 {
public static void main(String[] args) {
SpringApplication.run(Zuul_9527.class,args);
}
}
路径配置说明