本身,在学习之前,我也是对spring cloud 比较陌生,也不理解什么所谓的注册中心等内容。学完之后就想从一个小白的角度去讲,去着手了解这个spring cloud 是什么东西,一个篇章可能介绍不完,也没法完整地去解释这是个什么内容,因此我想通过几篇连载去讲述,当然会有很多不足,因此也欢迎大家私聊我给出意见。
Spirng Cloud(一 什么是微服务)
前言
大家可能在此之前多多少少都听说过微服务。微服务在最近几年也是很火,如果你不会微服务都可能都显得不那么主流了。甚至有很多团队强行为了微服务而去微服务,最终写成一个大型的分布式单体应用,就是改造后的系统既没有微服务的快速扩容,灵活发布的特性,也让原本的单体应用失去了方便开发,部署容易的特性(项目拆为多份,开发部署复杂度都提高了),不得不说是得不偿失。因此我们在了解SpringCloud 前非常有必要了解这是个什么东西,我们为什么使用他?怎么样设计更加合理。
一、微服务是什么?
首先呢,我认为了解一个陌生的东西,更好的是通过代码,通过实践去了解更为深刻,也更好了解。
不需要用多么专业的术语,因为我在一开始看到这些专业的术语我是懵逼的,虽然那些术语让人觉得很装逼,但是我更希望知道这是个什么东西更为重要。那么本次面向的读者呢也是刚要了解这部分内容的朋友,如果你是大佬,emmm,这文章肯定有很多不足,可以私聊我帮忙指点。
上面这是我在别的地方找到的一张图,可以清楚地看到这是个逐步解耦的过程。
解耦不解耦,怎么理解,怎么解释?微服务是什么样?
解耦就是分开,怎么分开,先看下没分开前,我们的工程目录是什么样。
大家注意看这个service、handler、mapper的包,此时都在一个工程中。
可以看到单体应用缺点:
部署成本高:无论是修改一行代码还是十行还是全部,所有的代码都需要替换。
改动影响大,风险高(耦合度高):在同一个工程里,你改动的内容可能有很多地方需要修改。
因为成本高,风险高,所以导致部署频率低(无法快速交付客户需求)
所以微服务的目的就是解决这些问题。
怎么做呢?就是将系统应由原来的单体变成几十到几百个不同的工程。
这就是所谓的解耦,让每个服务可以独立运行。每一个子工程都会部署在一台服务器上,这么多服务器我们便称之为服务集群。
因此我们就可以看到微服务的优点:
针对特定服务发布,影响小,风险小,成本低(因为服务分离了,增加服务只需加一个,对其它的不用全部更新)
频繁发布版本,快速交付需求
低成本扩容,弹性伸缩,适应云环境
当然,它也带来了很多缺点
分布式系统的复杂性
部署,测试和监控的成本问题
分布式事务和CAP的相关问题
产生服务间的依赖,服务如何拆封,内部接口规范,数据传递等问题,尤其是服务拆分,需要团队熟悉业务流程,懂得取舍,要保证拆分的粒度服务既符合“高内聚,低耦合”的基本原则,还要兼顾业务的发展以及公司的愿景,要还要说服团队成员为之努力,并且积极投入,在多方中间取得平衡。
二、踏出微服务(Spring Cloud)的第一步
既然都说了手摸手,那当然就是从创建工程开始,并且本次为了介绍微服务,所以会抛开dao层。使大家更清晰明了看到微服务的运转。首先肯定要自己装maven。
1、创建父工程
Create new project - maven - 勾选create from archetype - (maven-archetype-quickstart)-next
groupId 我这边就是org(个人)antry(我的网名)大家也可以和我一样,也可以用自己的。当然后面有的地方可能用到。注意一下就行,还有artifactId 是父工程名,我这边创建的父工程名时studycloud
选择自己的maven库
这就是文件存储位置
2、创建消费服务(custom-service)和user服务(user-service)
父工程创建好后我们先不急着配置,再创建两个子工程。
custom-service大家看这个名称可以理解这是一个消费者,因此这个user±service就是认为是生产者服务。再明了一点讲,我们一会儿就是要去访问custom的服务器,通过custom访问user服务。
后面其它选的和父工程是一样的,只有最后一步注意一下。
这个横杆需要自己加上,到这边它会没掉。
在每个子工程下创建resources文件夹
3、现在我们开始配置,配置成Spring Boot项目
当然了,学这个spring cloud 你要先去了解一下spring boot 了
下面话不多说,开始配置
3.1 父工程pom
添加 spring-boot parent标签
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
3.2 子工程pom
两个子工程一样
首先告诉系统子工程的父亲,因此添加parent标签
<parent>
<groupId>org.antry</groupId>
<artifactId>studycloud</artifactId>
<version>1.0</version>
</parent>
添加两个dependency
<!-- springboot 启动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 热启动依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
和一个plugin
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
3.3 custom-service启动类
现在我们先测试custom工程是否能够使用,那么我这边也介绍一个插件,大家可以安装一下,用于快速创建启动类。
我们需要配置,所以我们也勾选这个application.yml,它会在resources下生成
大家记得刷新maven,不然会找不到包。
3.4 custom-service配置文件
application.yml中我们先配置端口和服务名
server:
port: 9001
spring:
application:
name: custom-service
3.5 custom-service的service和control层
UserService接口
package org.antry.service;
/**
* @ClassName UserService
* @Description 接口
* @Autor TT
* @Date 2020/11/11 11:30
* @Version 1.0
*/
public interface UserService {
/**
* 返回
* @param id
* @return
*/
public String doGetUser(Long id);
}
UserServiceImpl实现类
package org.antry.service;
import org.springframework.stereotype.Service;
/**
* @ClassName UserService
* @Description 实现类
* @Autor T_Antry
* @Date 2020/11/11 11:26
* @Version 1.0
*/
@Service
public class UserServiceImpl implements UserService{
@Override
public String doGetUser(Long id){
return "custom-Service回应:" +
"<div><h1>T_Antry工作室-springcloud动手了解</h1></div>" +
"customService[无中转]:"+String.valueOf(id);
}
}
handler类
package org.antry.controller;
import org.antry.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName UserHandler
* @Description TODO
* @Autor T_Antry
* @Date 2020/11/11 11:32
* @Version 1.0
*/
@RestController
@RequestMapping("user")
public class UserHandler {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public String doGet(@PathVariable Long id){
return userService.doGetUser(id);
}
}
我们可以跑起来测试一下这个custom工程,现在还不是微服务,只是一个普通的spring boot 的工程。
运行起来之后,我们在url中输入 http://localhost:9001/user/123
我们就能够看到:
接下里,我们配置同样的步骤,也让user-service的工程也能够这样访问。当然,端口配置不同的端口,例如我这边就把它配置成1001,如下:
server:
port: 1001
spring:
application:
name: user-service
测试结果如下:
3.6 开始入手微服务
我们现在呢就是做到去访问custom-service服务器去访问user-service
那么我们是通过Restemplate去访问的,一开始我看到RestTemplate是非常懵逼的,这其实就是一个类,也不是什么东西,现在还不必太过纠结这是什么东西。只需要知道,我们用这个类的对象来拿到其它服务器的执行结果就可以了。
这次测试我们不需要修改user-service的工程,我们只需要修改custom-service的handler,以及启动类。
启动类新增Template配置
package org.antry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class CustomService {
public static void main(String[] args) {
SpringApplication.run(CustomService.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
修改handler类
这个控制层,我们原本调用的是custom-service这台服务器自己本身的服务,那么我们现在通过写死的url去访问user-service,并拿到结果再返回。
package org.antry.controller;
import org.antry.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @ClassName UserHandler
* @Description TODO
* @Autor T_Antry
* @Date 2020/11/11 11:32
* @Version 1.0
*/
@RestController
@RequestMapping("user")
public class UserHandler {
@Autowired
private RestTemplate restTemplate;
@Autowired
private UserService userService;
@GetMapping("/{id}")
public String doGet(@PathVariable Long id){
String url = "http://127.0.0.1:1001/user/"+id;
return "<div><h1>custom-Service回应:</h1><div>"+restTemplate.getForObject(url, String.class);
// return userService.doGetUser(id);
}
}
此时大家通过custom-service,就可以访问到user-service
以上的部分呢,其实我们已经可以看到服务之间可以通过这样的方式去联系,我想大家通过实践也能有自己的一些理解。
那么我也会把这一部分的源码上传,现在的这种方式当然大家也会认为它的不便捷,还要写死url等问题,我将会在进行解释,喜欢就点个赞吧。
本次例程资源
下一章
Spring cloud 理论+实践+解析 手摸手带你一起搭建(二 Eureka服务注册中心)