SpringCloud

1. Spring Cloud 是什么?

在学习本课程之前,读者有必要先了解一下 Spring Cloud

Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的开发便利性简化了分布式系统的开发,比如服务发现、服务网关、服务路由、链路追踪等。Spring Cloud 并不重复造轮子,而是将市面上开发得比较好的模块集成进去,进行封装,从而减少了各模块的开发成本。换句话说:Spring Cloud 提供了构建分布式系统所需的“全家桶”。

Spring Cloud 现状:
目前,国内使用 Spring Cloud 技术的公司并不多见,不是因为 Spring Cloud 不好,主要原因有以下几点:

Spring Cloud 中文文档较少,出现问题网上没有太多的解决方案。
国内创业型公司技术老大大多是阿里系员工,而阿里系多采用 Dubbo 来构建微服务架构。
大型公司基本都有自己的分布式解决方案,而中小型公司的架构很多用不上微服务,所以没有采用 Spring Cloud 的必要性。
但是,微服务架构是一个趋势,而 Spring Cloud 是微服务解决方案的佼佼者,这也是作者写本系列课程的意义所在。

Spring Cloud 优缺点:
其主要优点有:

集大成者,Spring Cloud 包含了微服务架构的方方面面。
约定优于配置,基于注解,没有配置文件。
轻量级组件,Spring Cloud 整合的组件大多比较轻量级,且都是各自领域的佼佼者。
开发简便,Spring Cloud 对各个组件进行了大量的封装,从而简化了开发。
开发灵活,Spring Cloud 的组件都是解耦的,开发人员可以灵活按需选择组件。
接下来,我们看下它的缺点:

项目结构复杂,每一个组件或者每一个服务都需要创建一个项目。
部署门槛高,项目部署需要配合 Docker 等容器技术进行集群部署,而要想深入了解 Docker,学习成本高。
Spring Cloud 的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习 Spring Cloud 是一个不错的选择。

 

2. 架构

2.1 集中式架构

优点:

系统开发速度快维护成本低

适用于并发要求较低的系统

缺点:

代码耦合度高,后期维护困难

无法针对不同模块进行针对性优化无法水平扩展

单点容错率低,并发能力差

 

2.2 分布式服务

优点:

将基础服务进行了抽取,系统间相互调用,提高了代码复用和开发效率

缺点:

系统间耦合度变高,调用关系错综复杂,难以维护

 

3. SpringCloud入门

3.1 父子工程的创建

3.1.1 创建父工程

【注:父子工程的创建为了演示子模块与子模块之间的调用】

【注:以及子模块如何注册到nacos中】

 

创建一个springboot的工程作为父工程:【这里的springboot版本使用的是2.4.2】

【注:父工程中可以什么都不用勾选】

【注:注意版本一定要对照】:

  构建好后在pom文件中加入版本控制器:

<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

    <spring-cloud.version>2020.0.1</spring-cloud.version>
    <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <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>
    </dependencies>
</dependencyManagement>

【注:这只是个父类工程,所以可以将src包删除,当然不删也行】

 

3.1.2 创建子工程

创建两个子类maven项目:分别为【test-serve】【test-comm

test-comm项目

【注:此项目为子类特有的公共项目,子类共同具有的都可以放在这个公共模块中】

因为我们要实现两个模块之间的调用,所以这里就以通过订单来查询商品信息

在此项目中添加依赖:

<!--        lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

声明一个商品实体类和一个固定返回格式的Result

 

Prodect商品实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Prodect {
    private Integer sid;
    private String sname;
    private Double price;
    private Integer snumber;
}

Result固定返回格式:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class result {
    private int code;
    private String msg;
    private Object data;
}

 

test-serve项目

【注:此项目用来管理子子类项目】

此项目pom文件依赖需要:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.3.2.RELEASE</version>
</dependency>

在此项目中继续创建两个maven子项目:【serve-pro】 //查询商品信息服务模块

在此项目中继续创建两个maven子项目:【serve-order】 //订单查询商品信息服务模块

 

serve-pro:

在此服务模块中的pom文件加入公共服务模块依赖:

<!--        注入公共模块依赖-->
        <dependency>
            <groupId>com.aaa</groupId>
            <artifactId>test-comm</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

在此服务模块中的main包下创建java包创建com.aaa包创建controller层创建ProdectController类

main -> java -> com.aaa -> controller -> ProdectController

在ProdectController中:

@RestController
public class ProdectController {

    @RequestMapping("/sep/{sid}")
public result SeleProdect(@PathVariable Integer sid){
    if(sid == 7){
        Prodect prodect = new Prodect(7,"娃哈哈",5.6,20);
        return  new result(200,"查询成功",prodect);
    }else if (sid == 8){
        Prodect prodect = new Prodect(8,"李宁风刃9000",599.0,10);
        return  new result(200,"查询成功",prodect);
    }

        Prodect prodect = new Prodect(8,"到特大姐夫700",890.0,15);
        return  new result(200,"查询成功",prodect);
}
}

【注:为了方便,这里就不连接数据库了,用的是固定数据】

在此项目中继续创建application.properties配置文件修改端口输入:

server.port=8081

在此项目继续创建主启动类:

【注:主启动类一定要在com.aaa包下】

@SpringBootApplication
public class ServeProdect {
    public static void main(String[] args) {
        SpringApplication.run(ServeProdect.class,args);
    }
}

 

serve-order:

在此服务模块中的pom文件加入公共服务模块依赖:

<!--        注入公共模块依赖-->
        <dependency>
            <groupId>com.aaa</groupId>
            <artifactId>test-comm</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

在此服务模块中的main包下创建java包创建com.aaa包创建controller层创建ProdectController类

main -> java -> com.aaa -> controller -> OrderController

在OrderController中:

@RestController
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;

    //根据订单号查询商品(模拟)
    @RequestMapping("/seleor/{eid}")
    public result SeleOrder(@PathVariable Integer eid){
        //通过订单查询模块将参数传递给商品信息模块且将结果返回
        result forObject =  restTemplate.getForObject("http://localhost:8081/sep/" + eid, result.class);
        //将返回的结果通过objectMapper中的convertValue()方法转换成商品信息对象 Prodect
        Object data = forObject.getData();
        ObjectMapper objectMapper = new ObjectMapper();
        Prodect prodect = objectMapper.convertValue(data, Prodect.class);
        return new result(200,"商品信息查询成功",prodect);
    }
}

在此项目中继续创建application.properties配置文件修改端口输入:

server.port=8082

在此项目继续创建主启动类:

【注:主启动类一定要在com.aaa包下】

@SpringBootApplication
public class ServeOrder {
    public static void main(String[] args) {
        SpringApplication.run(ServeOrder.class,args);
    }
    @Bean
    public RestTemplate getrestTemplate(){//这个是为了可以将两个服务模块之间的调用
        return new RestTemplate();
    }
}

 

3.2  nacos模块注册

这个需要软件nacos软件来启动服务

所需nacos百度云链接:https://pan.baidu.com/s/1uG2t3Mzbnp8T13ZTFegMkQ   提取码:6666

【注:这个可以在windowns中直接解压】

解压后进入目录中的bin目录

在此目录中进入cmd:

输入:

startup.cmd -m standalone

来启动nacos

浏览器输入 localhost:8848

首先会进入登录页面

账号和密码都是nacos

登录进入

直接点击此处发现是空很正常

这里需要对子服务模块进行nacos配置才会注册进来

 

需要在test-serve项目中添加所需nacos依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

在所需要注册的服务模块中的application配置文件中配置:

serve-pro:

在此服务模块中的application配置文件中配置:

spring.application.name=prodect
#设置注册中心的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848

 

在所需要注册的服务模块中的application配置文件中配置:

serve-order:

在此服务模块中的application配置文件中配置:

spring.application.name=order
#设置注册中心的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848

 

全部配置完后serve-po、serve-order 两个子模块需启动,如果已经启动需要重启

再次进入nacos网页刷新:

测试成功,两个子服务模块已经注册进来!!

 

4. 负载均衡

4.1 自定义策略

【注:这个已经过时,但是还是要简单测试一下】

test-serve项目下将serve-pro子服务模块复制一份为serve-po2

在serve-po2下修改一下application配置文件:

server.port=8083

 【注:这里只修改端口就行】

再次到ProdectController中:小修改一下代码以便一会测试区分:

 

test-serve项目下的pom文件中添加负载均衡所需依赖

//负载均衡
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

 

serve-order子服务模块下的OrderController中:

@RestController
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;
    //将负载均衡注入
    @Resource
    private LoadBalancerClient loadBalancerClient;

    //根据订单号查询商品(模拟)
    @RequestMapping("/seleor/{eid}")
    public result SeleOrder(@PathVariable Integer eid){
        //负载均衡
        ServiceInstance prodect1 = loadBalancerClient.choose("prodect");
        String host = prodect1.getHost();
        int port = prodect1.getPort();
        String url = "http://"+host+":"+port+"/sep/"+eid;
        
        result forObject =  restTemplate.getForObject(url, result.class);
        Object data = forObject.getData();
        ObjectMapper objectMapper = new ObjectMapper();
        Prodect prodect = objectMapper.convertValue(data, Prodect.class);
        return new result(200,"商品信息查询成功",prodect);
    }
}

 

4.2 OpenFeign

4.2.1 OpenFeign策略(轮询算法)

test-serve项目下的pom文件中添加openfeign所需依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在调用者的主启动类上加上:

@EnableFeignClients

注解

这里serve_order模块属于调用者,所以在其主启动类上加上开启OpenFeign注解

【注:serve_order是订单模块,serve_po是商品信息模块,以订单查询商品信息,所以serve_order是调用者一方】

 

serve_order模块下的com.aaa包下创建feign包中创建ProdectServiceFeign接口类

serve_order -> com.aaa -> feign -> ProdectServiceFeign

@FeignClient("prodect")
public interface OrderService {
    @RequestMapping("/sep/{eid}")
    public result getSeleOrder(@PathVariable Integer eid);
}

 

serve_order模块下的OrderController类中:

@RestController
public class OrderController {

    //将刚才写好的Serveice接口注入进来
    @Autowired
    private OrderService orderService;

    //根据订单号查询商品(模拟)
    @RequestMapping("/seleor/{eid}")
    public result SeleOrder(@PathVariable Integer eid){
        //OpenFeign
        result forObject = orderService.getSeleOrder(eid);
        Object data = forObject.getData();
        ObjectMapper objectMapper = new ObjectMapper();
        Prodect prodect = objectMapper.convertValue(data, Prodect.class);
        return new result(200,"商品信息查询成功",prodect);
    }
}

 

4.2.2 OpenFeign自定义策略(随机算法)

serve_order模块下的com.aaa包下创建config包中创建LoadBalancerConfig接口类

serve_order -> com.aaa -> config-> LoadBalancerConfig

public class LoadBalancerConfig {
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); //loadbalancer.client.name
        // 对应的需要进行负载均衡的名字是什么
        System.out.println("======"+name);
        // product
        return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

在调用者的主启动类上加上:

@LoadBalancerClient(value = "prodect",configuration = LoadBalancerConfig.class)

注解

这里serve_order模块属于调用者,所以在其主启动类上加上开启OpenFeign注解

【注:serve_order是订单模块,serve_po是商品信息模块,以订单查询商品信息,所以serve_order是调用者一方】

 

4.3 sentinel

【注:当子服务模块中被调用者其中一个出现异常时,这时我们该如何?】

【注:那么openfeign为我们提供一个兜底策略,就算哪怕子服务模块其中一个出现异常也不会直接向客户返回500这些信息】

test-serve项目下的pom文件中添加sentinel所需依赖:

<!-- sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

随后在application配置文件中开启openfeign对sentinel的支持

#??openfeign ?sentinel???
feign.sentinel.enabled=true

serve_order模块下的Serveice层创建一个ProdectServiceim实现类

serve_order -> Service -> ProdectServiceim

@Service
public class ProdectService implements OrderService{

    @Override
    public result getSeleOrder(Integer eid) {
        if (eid == 7){
            Prodect prodect = new Prodect(6,"百岁山",5.6,20);
            return  new result(200,"查询成功",prodect);
        }else if (eid == 8){
            Prodect prodect = new Prodect(5,"361度跑鞋",52.6,10);
            return  new result(200,"查询成功",prodect);
        }
        return null;
    }
}

【注:这里实现的是4.2.1下写的OrderService接口】

【注:写这个目的为如果子服务模块出现异常,那么sentinel会自动转到这里,从这里代替出异常的服务模块】

那么我们将serve-pro子服务模块下的ProdectController中的内容故意让他报异常进行测试:

运行测试:

两次请求结果皆不报异常,sentinel启动成功

说明sentinel兜底成功!!!


以上便是SpringCloud中的内容,如有漏缺请在下方留言告知,我会及时补充

posted @ 2023-09-21 10:56  九极致之术  阅读(18)  评论(0编辑  收藏  举报