SpringCloud学习笔记

本文详细介绍了SpringCloud的学习和项目实践,从微服务概念、SpringCloud组件介绍,到Eureka服务注册与发现、Ribbon负载均衡、服务熔断与降级、Nacos服务注册中心的使用,以及OpenFeign的初步实现,深入浅出地展示了SpringCloud在分布式系统中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、微服务

1.1 什么是微服务

  • 微服务化的核心是将传统的一站式应用,根据业务拆分成一个一个服务,彻底的祛耦合,每一个服务提供单个业务功能的服务,一个服务做一件事,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或者销毁,拥有自己独立的数据库

1.2 SpringCloud

  • SpringCloud,基于SpringBoot提供的一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,出了基于NetFix的开源组件做高度抽象封装外,还有一些选型中立的开源组件。
  • SpirngCloud利用SpringBoot的开发便利性,巧妙简化了分布式系统基础设施开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等,他们都可以用SpringBoot的开发风格做到一键启动和部署。
  • SpringBoot并没有重复造轮子,它只是将目前各家公司开发的比较成熟,经得起实际考研的服务框架组合起来,通过SpringBoot风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
  • SpringCloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。

1.3 分布式+服务治理Dubbo

二、SpringCloud项目

2.1 新建maven工程

  1. 删除src文件
  2. 配置pom
  <!-- 打包方式pom -->
  <packaging>pom</packaging>
  
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  
  <dependencyManagement>
  	<dependencies>
  		<!-- spingcloud依赖 -->
		<dependency>
		    <groupId>org.springframework.cloud</groupId>
		    <artifactId>spring-cloud-dependencies</artifactId>
		    <version>Greenwich.SR1</version>
		    <type>pom</type>
		    <scope>import</scope>
		</dependency>
		<!-- springboot -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-dependencies</artifactId>
			<version>2.1.4.RELEASE</version>
			<type>pom</type>
			<scope>import</scope>
		</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>1.3.2</version>
	    </dependency>
  	</dependencies>
  </dependencyManagement>

  	<!-- 打包插件 -->
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>
  1. 在当前项目下创建maven scloud-api module
  • 创建数据库表
CREATE TABLE `dept` (
	`deptno` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`dname` VARCHAR(60) ,
	`db_source` VARCHAR(60) 
)
COLLATE='utf8_general_ci'
;
INSERT INTO dept (dname,db_source) VALUES ("开发部",DATABASE());
INSERT INTO dept (dname,db_source) VALUES ("人事部",DATABASE());
INSERT INTO dept (dname,db_source) VALUES ("财务部",DATABASE());
INSERT INTO dept (dname,db_source) VALUES ("市场部",DATABASE());
INSERT INTO dept (dname,db_source) VALUES ("运维部",DATABASE());
  • 创建实体类
@Data
@NoArgsConstructor
@Accessors(chain = true) //链式写法
public class Dept implements Serializable{

	private Long deptno;
	private String dname;
	private String dbSource;
	public Dept(String dname) {
		this.dname = dname;
	}
	/*
	 * 链式写法:
	 * Dept dept = new Dept();
	 * dept.setDeptno(11).setDname('sss').setDbSource('001');
	 */
}
  1. 在当前项目下创建scloud-provider-dept-8001 module
  • 添加依赖
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
    <!-- 需要拿到实体类,配置api module -->
    <dependency>
    	<groupId>com.company</groupId>
    	<artifactId>scloud-api</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
    	<groupId>mysql</groupId>
    	<artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>druid</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>
    
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-devtools</artifactId>
    	<scope>runtime</scope>
		<optional>true</optional>
    </dependency>
    
  </dependencies>
  • 配置application.yml
server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.scloud.api.pojo
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    
#spring配置
spring:
  application:
    name: scloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/cloud01?serverTimezone=UTC&characterEncoding=utf-8
    username: root
    password: root
  • 添加对dept表的操作
  • 控制器相关
@RestController
@RequestMapping("/dept")
public class DeptController {
	@Autowired
	private DeptService deptService;
	@PostMapping("/add")
	public boolean addDept(Dept dept) {
		return deptService.addDept(dept);
	}
	
	@GetMapping("/get/{id}")
	public Dept getDeptById(@PathVariable("id")Long deptno) {
		return deptService.queryById(deptno);
	}
	@GetMapping("/list")
	public List<Dept> queryAll(){
		return deptService.queryAll();
	}
}

2.2 Rest环境搭建

  • 创建消费者scloud-consumer-dept-80 module
  • 添加依赖
  <dependencies>
    <!-- 实体类+web -->
    <dependency>
    	<groupId>com.company</groupId>
    	<artifactId>scloud-api</artifactId>
    	<version>0.0.1-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>
    	<scope>runtime</scope>
    	<optional>true</optional>
    </dependency>
    
  </dependencies>
  • 配置端口8080
  • 添加config
@Configuration
public class ConfigBean {
	@Bean
	public RestTemplate getRestTemplate() {
		return new RestTemplate();
	}
}
  • 添加controller
@RestController
@RequestMapping("/consumer")
public class DeptConsumerController {
	
	//理解:消费者不应该有service层
	//RestTemplate....供我们直接调用
	//(url,实体:Map,Class<T> responseType)
	@Autowired
	private RestTemplate restTemplate;
	
	private static final String REST_URL_PREFIX = "http://localhost:8001";
	
	@RequestMapping("/dept/add")
	public boolean add(Dept dept) {
		return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add", dept, Boolean.class);
	}
	
	@RequestMapping("/dept/get/{id}")
	public Dept get(@PathVariable("id") Long id) {
		//return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
		ResponseEntity<Dept> response=restTemplate.exchange(
				REST_URL_PREFIX+"/dept/get/{id}",
				HttpMethod.GET,
				null,
				Dept.class,
				id 
				);
		if(!response.getStatusCode().is2xxSuccessful()) {
			return null;
		}
		return response.getBody();
	}
	
	@RequestMapping("/dept/list")
	public List<Dept> list() {
		//return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list/",List.class);
		ResponseEntity<List<Dept>> response = restTemplate.exchange(
				REST_URL_PREFIX+"/dept/list/",
				HttpMethod.GET,
				null,
				new ParameterizedTypeReference<List<Dept>>() {
				});
		if(!response.getStatusCode().is2xxSuccessful()) {
			return null;
		}
		return response.getBody();
		
	}
}
  • 先运行provider,再运行consumer,然后测试嫩否通过consumer访问provider

2.3 Eureka服务注册与发现

2.3.1 创建Eureka Server

  • 导入依赖
	<dependency>
	    <groupId>org.springframework.cloud</groupId>
	    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
	</dependency>

	<dependency>
	    <groupId>org.glassfish.jaxb</groupId>
	    <artifactId>jaxb-runtime</artifactId>
	</dependency>
  • 配置application
server:
  port: 7001
  #port: 8761 默认端口
  
#Eureka配置
eureka:
  instance:
    #Eureka服务端实例名称
    hostname: localhost 
  client:
    #表示是否向eureka注册中心注册自己
    register-with-eureka: false 
    #false表示自己为注册中心
    fetch-registry: false 
    service-url: 
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  • 启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
	public static void main(String[] args) {
		SpringApplication.run(EurekaApplication.class, args)
	}

}

2.3.2 注册服务

  • 在服务提供者下添加依赖
    <dependency>
	    <groupId>org.springframework.cloud</groupId>
	    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
	</dependency>
    
    <!-- 完善监控信息 -->
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
  • 配置application
#Eureka配置
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    instance-id: sc-provider-dept8001
    
#info配置
info:
  app.name: company-springcloud
  company.name: supercokou
      
  • 启动类添加@EnableEurekaClient注解
  • 添加服务信息查找方法
	@Autowired
	private DiscoveryClient discoveryClient;
	//注册进来的微服务获取信息
	@GetMapping("/discovery")
	public Object discovery() {
		//获取微服务列表清单
		List<String> services=discoveryClient.getServices();
		System.out.println("discovery=>services:"+services);
		
		List<ServiceInstance> instances = discoveryClient.getInstances("SCLOUD-PROVIDER-DEPT");
		for(ServiceInstance instance:instances) {
			System.out.println(
					instance.getHost()+"\t"+
					instance.getPort()+"\t"+		
					instance.getUri())+"\t"+
					instance.getServiceId()
					);
		}
		return this.discoveryClient;
	}
  • 集群就是在defaultZone多注册几个地址就行了

CAP原则

C:Consistency 强一致性
A:Availability 可用性
P:Partition tolerance 分区容错性

2.4 负载均衡

2.4.1 ribbon

  1. 负载均衡简单分类:

    • 集中式LB :即在服务的消费方和提供方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务的
    • 进程式LB :将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。
  2. 使用Ribbon实现负载均衡

  • 在consumer模块添加依赖
	<dependency>
	    <groupId>org.springframework.cloud</groupId>
	    <artifactId>spring-cloud-starter-ribbon</artifactId>
	    <version>1.4.6.RELEASE</version>
	</dependency>

    <dependency>
	    <groupId>org.springframework.cloud</groupId>
	    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
	</dependency>
  • 配置application
#配置Eureka
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:7001/eureka
  • 在启动类添加@EnableEurekaClient
  • 在config添加
@Configuration
public class ConfigBean {
	@Bean
	@LoadBalanced  //配置负载均衡 
	public RestTemplate getRestTemplate() {
		return new RestTemplate();
	}
}
  • 在deptcontroller修改
	//Ribbon:地址是一个变量,通过服务名称来访问
	private static final String REST_URL_PREFIX = "http://SCLOUD-PROVIDER-DEPT";
  • 测试访问 http://localhost:8080/consumer/dept/list

2.5 服务熔断

  1. 服务熔断简单介绍
  • 服务熔断:当调用链路中某个服务不可用(断路)时,快速返回错误信息,不再继续调用后续的服务。
  • 服务降级:当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此保证核心服务可用,以及部分非核心服务可以降级使用。
  1. 什么是Hystrix
  • Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
  • ”断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

2.5.1 服务熔断实现

  • 复制一个服务提供者改名为scloud-provider-dept-hystrix-8001
  • 添加依赖
	<dependency>
	    <groupId>org.springframework.cloud</groupId>
	    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
	</dependency>
  • 添加备选方法
	@HystrixCommand(fallbackMethod = "getHystrixDeptById")
	@GetMapping("/get/{id}")
	public Dept getDeptById(@PathVariable("id")Long deptno) {
		Dept dept=deptService.queryById(deptno);
		if(dept==null) {
			throw new RuntimeException("id=>"+deptno+",不存在该用户,或者信息无法找到");
		}
		return dept; 
	}
	
	//备选方案
	public Dept getHystrixDeptById(@PathVariable("id")Long deptno,Throwable throwable) {
		return new Dept()
				.setDeptno(deptno)
				.setDname(throwable.getMessage())
				.setDbSource("no this database in MYSQL"); 
	}
  • 配置启动
@SpringBootApplication
@MapperScan("com.scloud.provider.mapper")
@EnableEurekaClient
@EnableCircuitBreaker //添加对熔断的支持
public class DeptProvider_hystrix_8001 {
	public static void main(String[] args) {
		SpringApplication.run(DeptProvider_hystrix_8001.class, args);
	}
}

  • 测试 http://localhost:8080/consumer/dept/get/6

2.6 服务降级

参考这篇文章
https://www.kancloud.cn/qingshou/aaa1/2667188

2.7 Nacos服务注册中心

2.7.1 nacos服务创建

  • 建立nacos环境配置文件
PREFER_HOST_MODE=hostname
MODE=standalone
SPRING_DATASOURCE_PLATFORM=mysql
MYSQL_SERVICE_HOST=192.168.150.128
MYSQL_SERVICE_DB_NAME=nacos
MYSQL_SERVICE_PORT=3306
MYSQL_SERVICE_USER=root
MYSQL_SERVICE_PASSWORD=123
MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
  • 创建nacos数据库
    • 配置文件复制到相应目录下
  • docker 配置nacos运行环境
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim
  • 使用docker logs -f nacos 查看启动日志

2.7.2 注册nacos服务

  • 添加依赖
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  • 注册服务
spring:
  application:
    name: scloud-provider-dept
  cloud:
    nacos:
      server-addr: 192.168.150.128:8848
  • 把之前用过Eureka相关的注释掉,才能正常启动

2.7.3 Nacos服务发现

步骤和上面一样

  • 负载均衡实现
	/**
	 * 通过DiscoveryClient 实现负债均衡
	 * @return
	 */
	public Object getUriString() {
		
		List<ServiceInstance> instances=discoveryClient.getInstances("scloud-provider-dept");
		ServiceInstance instance=instances.get(new Random().nextInt(instances.size()));
		return instance.getUri();
	}

2.8 OpenFeign

2.8.1 初步使用

  • 添加依赖
    <!-- OpenFeign -->
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-openfeign</artifactId>
	</dependency> 
	<!-- 负载均衡 -->   
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-loadbalancer</artifactId>
	</dependency>
  • 通过@EnableFeignClients注解开启Feign功能
  • 创建DeptService
@FeignClient("scloud-provider-dept")
@Service
public interface DeptService {
	@PostMapping("/dept/add")
	public boolean addDept(Dept dept);
	@GetMapping("/dept/get/{id}")
	public Dept getDeptById(@PathVariable("id")Long deptno);
	@GetMapping("/dept/list")
	public List<Dept> queryAll();
}

  • DeptController
@RestController
@RequestMapping("/consumer")
public class DeptConsumerController2 {
	@Autowired DeptService deptService;
	
	@RequestMapping("/dept/add")
	public boolean add(Dept dept) {
		return deptService.addDept(dept);
	}
	
	@RequestMapping("/dept/get/{id}")
	public Dept get(@PathVariable("id") Long id) {
		return deptService.getDeptById(id);
	}
	
	@RequestMapping("/dept/list")
	public List<Dept> list() {
		return deptService.queryAll();
	}
	
}

2.8.2 连接池

  • 引入依赖
<!--ok-html-->
	<dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-okhttp</artifactId>
	</dependency>
  • 开启连接池
fegin:
  okhttp:
    enabled: true
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值