SpringCloud系列入门(一)

前言

微服务

微服务的核心思想是模块化,标准化,解耦合。
举例来说,比如一家公司,只有一个部门,这个部门管理一切技术、运营、财务、法务。。。你觉得能行么?放到spring里面来,古老的单体服务就像一家只有一个部门的公司,微服务要做的,就是把这些部门拆开,规定每个部门的责任范围,沟通标准等等。
本教程立足于以下版本约定:

  1. springboot2.4.1
  2. springcloud2020.0.1

springcloud

SpringCloud不是一个技术,而是一组技术。这一组技术结合起来总称SpringCloud,是微服务思想的implement。

一组技术

究竟有哪几大类呢?

  1. 服务注册中心。consul,nacos,zookeeper等
  2. RPC调用框架,feign,thrift等
  3. 消息中间件,rabbitMQ,rocketMQ,Kafaka,zeroMQ等
  4. 服务熔断降级,sentinel,hystrix等
  5. 消息总线,bus等
  6. 安全,SpringSecurity,shiro等
  7. 网关,gateway等
  8. 配置中心,nacos,config等

从最简单的微服务开始

最简单的微服务简单到你不敢想,其实大多数技术都是这样,原理都不难,主要是层层加码以后可能会遇到非常复杂的协调性问题。

父工程

	<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring-cloud.version>2020.0.1</spring-cloud.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.4.1</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>
        </dependencies>
    </dependencyManagement>

就光看这个依赖就知道简单的呀批。

服务提供者

最简单的服务提供者是啥呢?就是一个普通springboot服务,甚至都不一定要引入springcloud的任何组件,就像这样:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

这样:

server:
  port: 8001
spring:
  application:
    name: service01

这样:

package yanyu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Desc
 * @Author yanyu
 * @Date 2021/2/20 5:01 下午
 **/
@SpringBootApplication
@RestController
public class SApp01 {

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

    @GetMapping("/msg")
    public String getMsg(){
        return "hello!";
    }
}

然后启动就行,超简单吧?

消费者

消费者也异常简单:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>        <!--
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <version>2.4.1</version>
        </dependency>
        <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>
    </dependencies>

其实openFeign完全不需要,从别的项目粘贴过来懒得删了
然后:

server:
  port: 8080
base:
  url: 'http://localhost:8001/msg'

然后:

package yanyu.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @Desc
 * @Author yanyu
 * @Date 2021/2/20 5:18 下午
 **/
@Configuration
public class RpcConfig {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

最后:

package yanyu;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @Desc
 * @Author yanyu
 * @Date 2021/2/20 5:17 下午
 **/
@SpringBootApplication
@RestController
public class CApp {

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

    @Resource
    private RestTemplate restTemplate;

    @Value("${base.url}")
    String url;

    @GetMapping("/")
    public String say(){
        return restTemplate.getForObject(url, String.class);
    }

}

然后启动,访问消费者的跟路径,就能看到hello!了,是不是简单的令人发指?

分析

虽然简单,但蕴含着微服务最基本的理念:拆分,通信。把不同的功能做成不同的包,然后定义好接口后直接远程调用。在本例中restTemplate实际上执行了rpc的功能。
最重要的,无论微服务怎样千变万化,最终落地,也就是:
不同springboot项目之间通过各种网络协议相互通信、相互协作的过程。

问题

上面的案例虽然提供了一些很宝贵的思想,但还存在很多很关键的问题:

  1. 如果存在多个服务提供者,多个服务消费者,那么互相之间调用服务就会变成一个复杂的蜘蛛网,最终一定会变成无法管理的泥潭,怎么办?
  2. 多个机器环境运行了多个服务提供者Pn,如果所有请求都打到P0上,那剩下的实例呢?一个服务有难,n-1个服务围观?怎么让请求均衡的打到各个机器上?
  3. 请求太多,服务处理不过来了,就要宕机了,怎么办?继续任由请求打进来?
  4. 如果我有一些必须按时序传递的消息要在不同服务之间传递,该怎么做?
  5. n个服务怎么统一配置文件?难道手动一个一个改?好蠢啊?
  6. 这么多微服务怎么做安全管理?一个服务写一套?

不要急,后面会一点点解答这些问题。

服务注册中心版本

上节提到,如果存在多个服务提供者,多个服务消费者,那么互相之间调用服务就会变成一个复杂的蜘蛛网,最终一定会变成无法管理的泥潭。怎么解决?
其实现实早就给我们提供了答案。如果各位对固定电话有一定的了解,就会知道为了避免n个用户之间需要天文数字的电话线互相连接的问题,使用了一种叫做交换机的东西。所有的电话线都连到交换机,你要拨打哪里,交换机就给你转哪里。
服务注册中心就是微服务的“交换机”。
常用的服务注册中心有老牌的zookeeper,consul,也有新生代的nacos。由于nacos还想还没支持到springboot2.4.1,本教学就先使用consul。

安装consul

如果你有linux虚拟机,就在虚拟机上安装一个docker(题外话,我再一次推荐各位用docker学习各种技术,太好用了,省心省事),拉一个consul的image下来,运行起来就行。

# 拉镜像
docker pull docker.io/consul
# 跑起来
docker run -d --name dc -p 8500:8500 docker.io/consul

就装好了,在宿主机上访问即可,效果如下:
在宿主机访问consul
当然,虚拟机的防火墙要关掉。

把微服务注册到consul

生产者*2

<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
    </dependencies>

两个服务的yaml:
一号生产

# 01
server:
  port: 8001
spring:
  application:
    name: consul-student-service
  cloud:
    consul:
      port: 8500
      host: {your host}
      discovery:
        service-name: ${spring.application.name}

二号生产一样的,只是把port换成8002。其中{your host}替换成你的consul地址。

然后是启动类:

package yanyu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Desc
 * @Author yanyu
 **/
@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class SApp01 {

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

    @GetMapping("/msg")
    public String getMsg(){
        return "hello by 8001!";
    }
}

二号服务只要把8001改成8002即可。
启动后可以在consul中发现这俩兄弟:
两个生产实例

消费者

走流程,pom:

<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <version>2.4.1</version>
        </dependency>
        <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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
    </dependencies>

yaml:

server:
  port: 8080
base:
  serviceName: consul-service
spring:
  application:
    name: consul-consumer
  cloud:
    consul:
      host: {your host}
      port: 8500
      discovery:
        service-name: ${spring.application.name}
        # 消费者不是必须注册到consul中
        register: false

由于这里涉及到多个生产实例,需要使用负载均衡,这个以后会讲,先用着就行:

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {
 
    @Bean
    @LoadBalanced // 负载均衡
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

启动类:

package yanyu;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @Desc
 * @Author yanyu
 **/
@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class CApp {

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

    @Resource
    private RestTemplate restTemplate;

    @Value("${base.serviceName}")
    String serviceName;

    @GetMapping("/")
    public String say(){
        return restTemplate.getForObject("http://" + serviceName + "/msg", String.class);
    }

}

启动起来,然后在浏览器或者curl工具或者postman啥的访问localhost:8080,
就会有下面两种情况:
一号服务
二号服务
很简单吧?
下一课会开始说openfeign,负载均衡和熔断机制,欢迎追更~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值