这里写目录标题
- 基本介绍
- 生产-消费-网关
- 父依赖
- 生产者服务
- 消费者服务
- 网关服务
- common服务
- 感想
基本介绍
Spring Cloud 是一个用于构建分布式系统和微服务架构的开发工具包。它提供了一系列的功能和组件,用于解决微服务架构中的常见问题,如服务注册与发现、负载均衡、配置管理、断路器等。本文将介绍如何使用 Spring Cloud 来构建一个简单的微服务架构。
服务注册与发现:
在微服务架构中,服务之间的相互调用是通过服务注册与发现来实现的。Spring Cloud 提供了 Eureka、Consul、ZooKeeper 等多种服务注册与发现的实现。我们可以选择其中一种来搭建服务注册中心,并在每个微服务启动时将自己注册到注册中心。
负载均衡:
在微服务架构中,负载均衡是将请求分发到多个服务实例上,以实现高可用和扩展性。Spring Cloud 提供了 Ribbon 作为默认的负载均衡解决方案,它可以与服务注册中心集成,自动实现负载均衡的功能。
配置管理:
在微服务架构中,配置管理可以帮助我们集中管理各个微服务的配置信息。Spring Cloud 提供了 Config Server 来实现统一的配置管理,将配置文件存储在 Git、SVN 等版本控制系统中,并通过统一的 API 接口提供给微服务使用。
断路器:
在微服务架构中,由于服务之间的相互调用,可能会出现某个服务不可用或响应超时的情况。为了保证系统的稳定性,我们可以使用断路器模式来处理这些故障。Spring Cloud 提供了 Hystrix 作为断路器的实现,可以在服务调用发生故障时进行降级或熔断处理。
生产-消费-网关
将一个服务拆分为生产、消费、网关三个微服务进行基本展示,实现了服务的拆分、注册、调用、网关、过滤,后期实现治理和分布式事务;
父依赖
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/>
</parent>
<artifactId>springcloud_demo</artifactId>
<packaging>pom</packaging>
<modules>
<module>service_1</module>
<module>service_2</module>
<module>gate_service</module>
<module>common</module>
<module>reminder</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<org.projectlombok.version>1.18.20</org.projectlombok.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
<java.version>8</java.version>
</properties>
<dependencies>
<!-- Spring Cloud 相关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- 其他 Spring Boot Starter 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!--spring cloud alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- lombok 管理 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
生产者服务
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>springcloud_demo</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test_service</artifactId>
<dependencies>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--单元测试-->
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
properties配置
server.port=9092
spring.application.name=service2
spring.cloud.nacos.server-addr=lh:8848
spring.cloud.config.import-check.enabled=false
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
基本生产
@RestController
@RequestMapping(path = "/hi")
public class HelloController {
@Resource
private HelloService helloService;
@RequestMapping(path = "/sy", method = RequestMethod.GET)
public String sy(){
// 1.根据服务名称,拉取服务的实例列表
helloService.sayHi();
return "service-2 say hi";
}
}
消费者服务
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>springcloud_demo</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test_2</artifactId>
<dependencies>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--单元测试-->
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
<scope>compile</scope>
</dependency>
<!--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>
<!--ok-http-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
</dependencies>
</project>
server.port=9091
spring.application.name=service1
spring.cloud.nacos.server-addr=lh:8848
spring.cloud.config.import-check.enabled=false
feign.okhttp.enabled=true
logging.level.com.com.han=debug
import com.han.config.DefaultFeignConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients(basePackages = "com.han.remote", defaultConfiguration = DefaultFeignConfig.class)
@SpringBootApplication
public class Test2Application {
public static void main(String[] args) {
SpringApplication.run(Test2Application.class, args);
}
}
配置类
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfig {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.FULL;
}
}
``@FeignClient(value = "service2")
public interface Service2Client {
@RequestMapping(method = RequestMethod.GET, path = "/hi/sy")
String getHi();
}
```java
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
controller
@RestController
@RequestMapping(path = "/hello")
public class HelloController {
@Resource
private HelloService helloService;
@Resource
private Service2Client service2Client;
@RequestMapping(method = RequestMethod.GET, path = "/sy")
public void sy(){
// 1.根据服务名称,拉取服务的实例列表
System.out.println(service2Client.getHi());
helloService.sayHi();
}
}
消费service
@Service
public class HelloServiceImpl implements HelloService {
private final DiscoveryClient discoveryClient;
@Resource
private RestTemplate restTemplate;
@Resource
private Service2Client service2Client;
public HelloServiceImpl(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
@Override
public void sayHi() {
System.out.println("service-1 say hi");
System.out.println(service2Client.getHi());
}
public void sayHiByRestTemplate() {
System.out.println("service-1 say hi");
List<ServiceInstance> instances = discoveryClient.getInstances("service_2");
// 2.负载均衡,挑选一个实例
ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));
// 3.获取实例的IP和端口
ResponseEntity<String> response = restTemplate.exchange(
instance.getUri() + "/hi",
HttpMethod.GET,
null,
new ParameterizedTypeReference<String>() {
},
new Object());
System.out.println(response.getBody());
System.out.println(response);
}
}
网关服务
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>springcloud_demo</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gate_service</artifactId>
<dependencies>
<!--网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>common</artifactId>
<version>2.7.12</version>
</dependency>
</dependencies>
</project>
server.port=9090
spring.application.name=gateway
spring.cloud.nacos.server-addr=lh:8848
spring.cloud.config.import-check.enabled=false
logging.level.com.com.han=debug
logging.pattern.dateformat=HH:mm:ss:SSS
logging.file.path="logs/${spring.application.name}"
spring.cloud.gateway.routes[0].id=service1
spring.cloud.gateway.routes[0].uri=lb://service1
spring.cloud.gateway.routes[0].predicates[0]=Path=/hello/**,/user/**
spring.cloud.gateway.routes[0].filters[0]=PrintAny
spring.cloud.gateway.routes[1].id=service2
spring.cloud.gateway.routes[1].uri=lb://service2
spring.cloud.gateway.routes[1].predicates[0]=Path=/hi/**
spring.main.web-application-type=reactive
@SpringBootApplication
public class GateApplication {
public static void main(String[] args) {
SpringApplication.run(GateApplication.class, args);
}
}
@Component
public class PrintAnyGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
@Override
public GatewayFilter apply(Object config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求
ServerHttpRequest request = exchange.getRequest();
// 编写过滤器逻辑
System.out.println("过滤器执行了");
// 放行
return chain.filter(exchange);
}
};
}
}
common服务
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>springcloud_demo</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common</artifactId>
<dependencies>
<!-- Lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1.获取请求头中的用户
String userInfo = request.getHeader("user-info");
// 2.判断是否为空,不为空,存入Usercontext
if (StrUtil.isNotBlank(userInfo)) {
}
// 3.放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception{
// 清除用户
// UserContext.removeUser();
}
}
package com.han.utils;
import lombok.Data;
@Data
public class Result<T> {
//状态码
private Integer code;
//信息
private String message;
//数据
private T data;
//构造私有化
private Result() { }
//设置数据,返回对象的方法
public static<T> Result<T> build(T data,Integer code,String message) {
//创建Resullt对象,设置值,返回对象
Result<T> result = new Result<>();
//判断返回结果中是否需要数据
if(data != null) {
//设置数据到result对象
result.setData(data);
}
//设置其他值
result.setCode(code);
result.setMessage(message);
//返回设置值之后的对象
return result;
}
//设置数据,返回对象的方法
public static<T> Result<T> build(T data,ResultCodeEnum resultCodeEnum) {
//创建Resullt对象,设置值,返回对象
Result<T> result = new Result<>();
//判断返回结果中是否需要数据
if(data != null) {
//设置数据到result对象
result.setData(data);
}
//设置其他值
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
//返回设置值之后的对象
return result;
}
//成功的方法
public static<T> Result<T> ok(T data) {
Result<T> result = build(data, ResultCodeEnum.SUCCESS);
return result;
}
//失败的方法
public static<T> Result<T> fail(T data) {
return build(data,ResultCodeEnum.FAIL);
}
public static<T> Result<T> success(T data){
return build(data, ResultCodeEnum.MY_CONDITION);
}
}
/**
* 统一返回结果状态信息类
*
*/
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(201, "失败"),
MY_CONDITION(666, "JUST 6"),
;
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
感想
框架的引入,让我们可以只需要导包就可以完成95%的工作;
但是如何抛开框架,我们是否可以自己完成spring、springcloud这些框架的基本功能那?
技术的提升不会仅仅是使用这些api接口,我们后面会尝试自己设计一些框架去替代