SpringCloud入门

2022-03-24 15:50:34 浏览数 (1)

产生背景

系统架构演变

集中式架构

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是影响项目开发的关键。

存在的问题:

代码耦合,开发维护困难 无法针对不同模块进行针对性优化 无法水平扩展 单点容错率低,并发能力差

垂直拆分

当访问量逐渐增大,单一应用无法满足需求,此时为了应对更高的并发和业务需求,我们根据业务功能对系统进行拆分

优点

系统拆分实现了流量分担,解决了并发问题 可以针对不同模块进行优化 方便水平扩展,负载均衡,容错率提高

缺点:

系统间相互独立,会有很多重复开发工作,影响开发效率

分布式服务

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式调用是关键。

优点:

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

缺点:

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

服务治理(SOA)

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键 以前出现了什么问题?

  • 服务越来越多,需要管理每个服务的地址
  • 调用关系错综复杂,难以理清依赖关系
  • 服务过多,服务状态难以管理,无法根据服务情况动态管理

服务治理要做什么?

服务注册中心,实现服务自动注册和发现,无需人为记录服务地址 服务自动订阅,服务列表自动推送,服务调用透明化,无需关心依赖关系 动态监控服务状态监控报告,人为控制服务状态

缺点:

服务间会有依赖关系,一旦某个环节出错会影响较大 服务关系复杂,运维、测试部署困难,不符合DevOps思想

微服务

前面说的SOA,英文翻译过来是面向服务。微服务,似乎也是服务,都是对系统进行拆分。因此两者非常容易混淆,但其实缺有一些差别。

微服务的特点:

单一职责:微服务中每一个服务都对应唯一的业务能力,做到单一职责

微:微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。

面向服务:面向服务是说每个服务都要对外暴露服务接口API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供Rest的接口即可。

自治:自治是说服务间互相独立,互不干扰。

团队独立:每个服务都是一个独立的开发团队,人数不能过多。

技术独立:因为是面向服务,提供Rest接口,使用什么技术没有别人干涉。

前后端分离:采用前后端分离开发,提供统一Rest接口,后端不用再为PC、移动段开发不同接口

数据库分离:每个服务都使用自己的数据源。

部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护。

远程调用方式

常见的远程调用方式有以下几种:

RPC:Remote Produce Call远程过程调用,类似的还有RMI。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型。

Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议。也可以用来进行远程服务调用。缺点是消息封装臃肿。 现在热门的Rest风格,就可以通过http协议来实现。

RPC

RPC,即 Remote Procedure Call(远程过程调用),是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。说得通俗一点就是:A计算机提供一个服务,B计算机可以像调用本地服务那样调用A计算机的服务。

Http

Http协议:超文本传输协议,是一种应用层协议。规定了网络传输的请求格式、响应格式、资源定位和操作的方式等。但是底层采用什么网络传输协议,并没有规定,不过现在都是采用TCP协议作为底层传输协议。说到这里,大家可能觉得,Http与RPC的远程调用非常像,都是按照某种规定好的数据格式进行网络通信,有请求,有响应。没错,在这点来看,两者非常相似,但是还是有一些细微差别。

RPC并没有规定数据传输格式,这个格式可以任意指定,不同的RPC协议,数据格式不一定相同。 Http中还定义了资源定位的路径,RPC中并不需要 最重要的一点:RPC需要满足像调用本地服务一样调用远程服务,也就是对调用过程在API层面进行封装。Http协议没有这样的要求,因此请求、响应等细节需要我们自己去实现。

  • 优点:RPC方式更加透明,对用户更方便。Http方式更灵活,没有规定API和语言,跨语言、跨平台
  • 缺点:RPC方式需要在API层面进行封装,限制了开发的语言环境。

因此,两者都有不同的使用场景:

如果对效率要求更高,并且开发过程使用统一的技术栈,那么用RPC还是不错的。 如果需要更加灵活,跨语言、跨平台,显然http更合适

那么我们该怎么选择呢?

微服务,更加强调的是独立、自治、灵活。而RPC方式的限制较多,因此微服务框架中,一般都会采用基于Http的Rest风格服务。

Http客户端工具

介绍

HttpClient是Apache公司的产品,是Http Components下的一个组件。

特点

基于标准、纯净的Java语言。实现了Http1.0和Http1.1 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE) 支持HTTPS协议。 通过Http代理建立透明的连接。 自动处理Set-Cookie中的Cookie。

Spring的RestTemplate

Spring提供了一个RestTemplate模板工具类,对基于Http的客户端进行了封装,并且实现了对象与json的序列化和反序列化,非常方便。RestTemplate并没有限定Http的客户端类型,而是进行了抽象,目前常用的3种都有支持:

HttpClient OkHttp JDK原生的URLConnection(默认的)

首先注册一个RestTemplate对象,可以在启动类位置注册

代码语言:javascript复制
@SpringBootApplication
public class HttpDemoApplication {

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

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

在测试类中注入

代码语言:javascript复制
@RunWith(SpringRunner.class)
@SpringBootTest(classes = HttpDemoApplication.class)
public class HttpDemoApplicationTests {

	@Autowired
	private RestTemplate restTemplate;

	@Test
	public void httpGet() {
		User user = this.restTemplate.getForObject("http://localhost:8088/user/1", User.class);
		System.out.println(user);
	}

}

通过RestTemplate的getForObject()方法,传递url地址及实体类的字节码,RestTemplate会自动发起请求,接收响应,并且帮我们对响应结果进行反序列化。

SpringCloud

SpringCloud是Spring旗下的项目之一,官网地址:点击

Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。

SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:

netflix

Eureka:注册中心 Zuul:服务网关 Ribbon:负载均衡 Feign:服务调用 Hystix:熔断器

微服务场景模拟

服务提供者

我们新建一个项目,对外提供查询用户的服务。 使用spring脚手架快速搭建 不懂的可以看这里下半部分springboot快速搭建 server-demo导入依赖

代码语言:javascript复制
    <modules>
        <module>user-service</module>
        <module>consumer-demo</module>
        <module>eureka-server</module>
    </modules>
    <packaging>pom</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
        <mapper.starter.version>2.0.3</mapper.starter.version>
        <pageHelper.starter.version>1.2.5</pageHelper.starter.version>
        <leyou.latest.version>1.0.0-SNAPSHOT</leyou.latest.version>
        <fastDFS.client.version>1.26.1-RELEASE</fastDFS.client.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!--springCloud-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--通用mapper启动器-->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>${mapper.starter.version}</version>
            </dependency>
            <!--分页助手paperHelper-->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>${pageHelper.starter.version}</version>
            </dependency>

            <!--mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!--FastDFS客户端-->
            <dependency>
                <groupId>com.github.tobato</groupId>
                <artifactId>fastdfs-client</artifactId>
                <version>${fastDFS.client.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

    </dependencies>

pojo类

代码语言:javascript复制
@Table(name = "user")
@Data
public class User implements Serializable {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Integer id;            // id
    private String name;    // 用户名
    private Integer age;    // 登录名
    private String email;    // 密码
}

映射mapper

代码语言:javascript复制
public interface UserMapper extends Mapper<User> {
}

service层

代码语言:javascript复制
/**
 * 服务提供方
 * 这是一个新建的类
 */
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public User findById(Long id){
        return userMapper.selectByPrimaryKey(id);
    }
   @Transactional//导入jdbc依赖的时候 事务已经导入进来了
   // 我们只需要在需要事务的方法上加入Transactional注解
    public void insertUser(User user){
        userMapper.insert(user);
    }

}

controller层

代码语言:javascript复制
@Slf4j
@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("{id}")
    @ResponseBody
    public User hello(@PathVariable("id") Long id){
       return userService.findById(id);
    }
}

启动器

代码语言:javascript复制
@SpringBootApplication
@MapperScan("cn.rpf.user.mapper")
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class);
    }
}

配置文件

代码语言:javascript复制
server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis
    username: root
    password: 123456
mybatis:
  type-aliases-package: cn.rpf.user.pojo

服务的调用者condumer-demo模组

导入依赖

代码语言:javascript复制
<dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>

pojo

代码语言:javascript复制
@Table(name = "user")
@Data
public class User implements Serializable {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Integer id;            // id
    private String name;    // 用户名
    private Integer age;    // 登录名
    private String email;    // 密码
}

控制层

代码语言:javascript复制
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;

   @GetMapping("{id}")
    public User queryUserById(@PathVariable("id") Long id){
    
       String url = "http://127.0.0.1/user/"   id;
        return this.restTemplate.getForObject(url, User.class);
    }
}

启动类

代码语言:javascript复制
@SpringBootApplication
public class UserConsumerDemoApplication {

    @Bean
    public RestTemplate restTemplate() {
        // 这次我们使用了OkHttp客户端,只需要注入工厂即可
        return new RestTemplate();
    }

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

测试

访问

查询出来

0 人点赞