传统软件行业
互联网软件行业
微服务
单体架构
一个归档包(可以是JAR、WAR、EAR或其它归档格式)包含所有功能的应用程序,通常称为**
单体应用。
**
优点
缺点
2014 ,martin fowler马丁·福勒
提出的:微服务架构**风格
** 常听的:分布式微服务
优点: 一个应用拆分为一组小型服务 每一个服务: 运行在自己的进程内,也就是可独立部署和升级,拥有自己独立的数据库。通过HTTP的方式进行互通; • 优点 • 服务围绕业务功能拆分 • 可以由全自动部署机制独立部署 • 微服务能够被小团队单独开发,这个小团队是2到5人的开发人员组成。 • 微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的。 • 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术;
只要实现功能即可~
•微服务只是业务逻辑的代码,不会和HTML,CSS 或其他界面组件混合。
•每个微服务都有自己的存储能力,可以有自己的数据库。也可以有统一数据库。
上图:**微服务分布式架构,每一个点表示一个功能, 使用时只需要调用需要的功能模块组合即可!
**
缺点:
分布式微服务架构中,把一个应用差分为多个 功能模块; 每个功能模块专注做自己的事情…
当需要实现某一功能时候, 只需要对多个需要的 功能模块直接相互调用即可…
那么每个模块即是 Provider也是Consumer
**(提供者和调用者)…**
什么叫RPC
远程过程调用
是一种进程间通信方式,他是一种技术的思想,而不是规范。
它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,
而不用程序员显式编码这个远程调用的细节。
即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。即网络通信
什么是REST
架构风格
**,指的是一组架构约束条件和原则。一个应用拆分为多个不同的功能模块,**做不同的事情
**!
一台服务器,承受不住某个工作,为了减轻服务器压力,做集群 负载均价…多个设备做同一见事情!
选型依据
当前各大IT公司用的微服务架构有哪些?
各微服务框架对比
《分布式系统原理与范型》定义:
“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”
分布式系统(distributed system)是建立在网络之上的软件系统。
单一应用架构——垂直应用架构——分布式服务架构——流动计算架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,
以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
适用于小型网站,小型管理系统,将所有功能都部署到一个功能里,简单易用。
缺点:
性能扩展比较难 协同开发问题 不利于升级维护...
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用
以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
通过切分业务来实现各个模块独立部署:
降低了维护和部署的难度,团队各司其职更易管理,性能扩展也更方便,更有针对性。
缺点: 公用模块无法重复利用,开发性的浪费
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,
作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。
此时,用于提高业务复用及整合的**分布式服务框架(RPC)
**是关键。
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,
此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。
提高机器利用率的资源调度 和 治理中心**(SOA)[ Service Oriented Architecture]
**是关键。
为各个微服务之间提供
配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务…依赖的关系
**分布式服务架构
为了方便开发使用 Maven高级应用...
开发项目:当然你也可以不用Maven来测试,多起几个项目工程...
这里删掉主工程的src (反之也用不上
主工程的**pom.xml
**中加入公共依赖
<?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>
<groupId>org.example</groupId>
<artifactId>SpringCloudBJ</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<!-- Maven父工程包含管理的子工程... -->
<modules>
<module>common_api</module>
<module>common_orderService</module>
<module>common_userService</module>
</modules>
<!-- SpringBoot父依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<!-- 统一设置Maven项目编码 jdk环境 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- spring-boot的web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring-boot的应用日志依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!-- spring-boot的单元测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok的依赖组件,别忘了要下载Idea lombok的插件哦~ -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- 指定SpringCloud配置必要!! -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version> <!-- 版本... -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- maven的默认下载资源.. -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
SpringCloud版本更新非常快!A B C D E F G H**目前最高已经到H了
**我凑!!
2021年初
主要存放项目开发所需要的实体类…
这里只有一个实体类…没有什么太复杂的操作
Order.Java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@AllArgsConstructor
@RequiredArgsConstructor
//创建一个实体类: 并根据lombok 及其注解生产对应的 构造函数,toString(); get/set...
public class Order {
private Integer id;
private String title;
private Double price;
private Integer uid;
}
pom.xml
不用任何添加或改动…看一下我其它Maven子工程如果调用;
<?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>SpringCloudBJ</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common_api</artifactId>
</project>
编写订单模块相对于的代码…
pom.xml
<?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>SpringCloudBJ</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common_orderService</artifactId>
<!-- 关键!! -->
<dependencies>
<!-- common_orderService工程引入common_api -->
<dependency>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>common_api</artifactId>
</dependency>
</dependencies>
</project>
OrderService.Java
import com.wsm.entity.Order;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class OrderService {
//模拟假订单集合...
private static List<Order> orders = new ArrayList<>();
//类加载时候默认获取一些数据..模拟假订单..
static {
orders.add(new Order(1, "订单11", 10.0, 1));
orders.add(new Order(2, "订单21", 10.0, 2));
orders.add(new Order(3, "订单31", 10.0, 1));
orders.add(new Order(4, "订单41", 10.0, 2));
}
//根据输入用户id获取当然用订单集合..
public List<Order> findOrderByUser(Integer uid) {
List<Order> myorders = new ArrayList<>();
//遍历集合从中获取 uid一致的数据存放在集合中返回...
for (Order order : orders) {
if (order.getUid() == uid) {
myorders.add(order);
}
}
return myorders;
}
}
OrderController.Java
import com.wsm.entity.Order;
import com.wsm.service.OrderService;
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.RestController;
import java.util.List;
@RestController
public class OrderController {
@Autowired
private OrderService os;
//REST风格进行请求..
@GetMapping("/findOrderByUser/{uid}") //参数是以 /请求名/{参数1}/{参数2} 进行访问的...
public List<Order> findOrderByUser(@PathVariable Integer uid){
//REST风格参数需要使用 @PathVariable获取!!
return os.findOrderByUser(uid);
}
}
SpringBoot的主工程类:**MyOrderServer.Java
**
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyOrderServer {
public static void main(String[] args) {
SpringApplication.run(MyOrderServer.class, args);
}
}
SpringBoot application.yml
**配置文件**
server:
port: 6001 #设置端口6001 当有多个微服务时候注意端口号可别冲突了..
spring:
application:
name: order-server #设置当然微服务名,后面的 注册/调用服务,都是根据这个来的;
编写用户模块相对于的代码…
pom.xml
<?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>SpringCloudBJ</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common_userService</artifactId>
<dependencies>
<!-- common_userService工程引入common_api -->
<dependency>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>common_api</artifactId>
</dependency>
</dependencies>
</project>
UserService.Java
@Service
@Slf4j //加载lo4g使用...
public class UserService {
@Autowired
private RestTemplate restTemplate;
//通过restTemplate进行网络通信,返回..其它远程模块的数据(虽然现在都是本地的不过就是模拟了..)
public List<Order> currentUserOrder(Integer uid) {
log.info("用户服务调用订单服务");
//硬编码的调用!! 这里的请求都写死的...这好吗?这不好...后面修改;
String myurl = "http://localhost:6001/findOrderByUser/" + uid;
log.info(myurl
);
List<Order> list = restTemplate.getForObject(myurl, List.class);
return list;
}
}
UserController.Java
//编程控制层,接受请求响应结果
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/showUserOrder")
public List<Order> showUserOrder(Integer uid){
return userService.currentUserOrder(uid); //调用Service层
//Service又是通过网络调用 订单模块来响应的结果,因此在此基础上要缺点订单模块是运行中的...
}
}
SpringBoot的主工程类:**MyUserServer.java
**
@SpringBootApplication
public class MyUserServer {
public static void main(String[] args) {
SpringApplication.run(MyUserServer.class, args);
}
@Bean
//实例化 RestTemplate 方便Service
//可别忘了@SpringBootApplication复合注解底层可以有@SpringBootConfiguration 它也是一个Spring的配置类!
public RestTemplate createRestTemplate() {
return new RestTemplate();
}
}
SpringBoot application.yml
**配置文件**
server:
port: 6002 #设置端口6002 当有多个微服务时候注意端口号可别冲突了..
spring:
application:
name: user-server #设置当然微服务名,后面的 注册/调用服务,都是根据这个来的;
前文已经编写了三个基础的微服务,在用户下单时需要调用商品微服务获取商品数据。
商品微服务提供了供人调用的HTTP接口
Spring框架提供的RestTemplate类可用于在应用中调用rest服务
它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接
至此已经可以通过RestTemplate调用商品微服务的RESTFul API接口。
但是我们把提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:
注册中心
**动态的对服务注册和服务发现以上就是一个典型的**分布式模块架构
** 多模块业务之间的相互调用 但↓↓↓↓
因为是用户模块—引用—订单模块
订单是提供者**provide
** 用户是调用者**Consumer
**
注意!
为了解决这种问题, 注册中心
**就出现了**
注册中心可以说是微服务架构中的 ”通讯录“,它记录了服务和服务地址 映射关系在分布式架构中
服务会注册到这里,当服务需要调用其它服务时,就这里找到服务的地址,进行调用。
服务注册中心是微服务架构非常重要的一个组件,在微服务架构里主要起到了协调者的一个作用。
注册中心一般包含如下几个功能:
注册中心
**
配置订阅:服务提供者和服务调用者订阅微服务相关的配置
配置下发:主动将配置推送给服务提供者和服务调用者心跳♥
**告诉注册中心我还在能干活
当然注册中心一般都有自我保护机制…
检测服务提供者的健康情况Spring Cloud 封装了 Netflix 公司开发的 Eureka
Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。
Eureka Server
**和**Eureka Client
**服务
**
各个节点启动后,会在EurekaServer中进行注册,
这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,
服务节点的信息可以在界面中直观的看到EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
**使用时先启动注册中心——Provider提供者——Consumer调用者
流动计算架构
基于上面的**SpringCloud的分布式服务架构
**开发
注册中心
pom.xml
<?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>SpringCloudBJ</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server</artifactId>
<!-- Eureka-server服务依赖,必须! -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 7001 #设置注册中心的 端口;
spring:
application:
name: eureka-server #注册中心应用名称;
#配置注册中心....
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/ #注册中心对外暴漏的注册地址...
#要不要去注册中心获取其他服务的地址,默认为true (目前只有一个注册中心而且,当前就是注册中心..)
fetch-registry: false
#是否将允许自己注入注册中心,默认为true (目前只有一个注册中心而且,当前就是注册中心..自己注册自己?)
register-with-eureka: false #如果一个应用中有两个注册中心可以开启,达到集群注册中心的目的...
主程序类**MyEurekaServer.Java
**
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //注解主程序开启 Eureka服务
public class MyEurekaServer {
public static void main(String[] args) {
SpringApplication.run(MyEurekaServer.class, args);
}
}
启动注册中心的服务, 表示当前项目作为SpringCloud中的注册中心
pom.xml
<!--- 注册服务的依赖.. -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
以订单的主程序为例子, 其它也都这么做
MyOrderServer.Java
@SpringBootApplication
@EnableEurekaClient
public class MyOrderServer {
public static void main(String[] args) {
SpringApplication.run(MyOrderServer.class, args);
}
}
@EnableEurekaClient 启动注册中心客户端
表示当前主程序注册到注册中心去…
@EnableDiscoveryClient,一种为@EnableEurekaClient,用法上基本一致。
注解
配置文件配置注册中心 Eureka
(以业务order的为例子)
application.yml
server:
port: 6001 #设置端口6001 当有多个微服务时候注意端口号可别冲突了..
spring:
application:
name: order-server #设置当然微服务名,后面的 注册/调用服务,都是根据这个来的;
eureka:
client:
service-url: #修改注册中心不上本地的ip 更改为无线网的ip 实现多设备连接...
defaultZone: http://localhost:7001/eureka/ #指定服务注册的注册中心;
instance:
prefer-ip-address: true #在Eureka上使用ip号..
instance-id: ${spring.cloud.client.ip-address}:${server.port} #在Eureka上显示ip号..
硬编码解决了
UserService.Java
import com.wsm.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient; //这个DiscoveryClient别搞错
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@Service
@Slf4j //加载lo4g使用...
public class UserService {
@Autowired
private RestTemplate restTemplate;
//通过restTemplate进行网络通信,返回..其它远程模块的数据(虽然现在都是本地的不过就是模拟了..)
@Autowired //实现动态调用...
private DiscoveryClient discoveryClient;
public List<Order> currentUserOrder(Integer uid) {
//获取注册中心上的微服模块实例 根据服务名;
//返回一个集合: 有可能这个服务在多个注册中心上存在,`负载均衡~` 所以是一个集合;
List<ServiceInstance> instances = discoveryClient.getInstances("order-server");
ServiceInstance serviceInstance = instances.get(0); //而本次只有一个...
log.info("用户服务调用订单服务");
//动态调用服务 服务的host 服务端口号 这个就是服务controller请求 给其参数uid
String myurl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/findOrderByUser/" + uid;
log.info(myurl);
List<Order> list = restTemplate.getForObject(myurl, List.class);
return list;
}
}
动态的从注册中心Eureka上获取ip
查看这里的 ip 192.168.1.1
但我这个目前没有弄无线局域网配置器...
**可以尝试~
在服务客户端Client 配置的~
默认 30秒心跳💓 90秒续约时间…
服务客户端Client 配置的 .yml
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/ #指定服务注册的注册中心;
instance:
prefer-ip-address: true #在Eureka上显示ip号..
instance-id: ${spring.cloud.client.ip-address}:${server.port} #在Eureka上显示ip号..
lease-renewal-interval-in-seconds: 5 #客户端向注册中心发送心跳时间
lease-expiration-duration-in-seconds: 10 #如果没有发送心跳的延迟续约时间...
如果客户端的服务因为某些原因关闭了。Eureka会根据心跳检测到你没了而移除你的服务…
注册中心
发送心跳如果超过时间没有发送 默认30秒两分钟~
如果还没有启动Eureka会把服务剔除…Eureka自我保护机制
服务并不会真的 移除…而是会给你一个时间如果还没有回来~则报错!一般不会关闭自我保护机制,因为如果服务又好了又可以立刻注册会来使用...
在服务端即 注册中心
**关闭保护机制。。。**
注册中心的**.yml
**
#配置注册中心....
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/ #注册中心对外暴漏的注册地址...跟随注册中心 提供的一致~
#要不要去注册中心获取其他服务的地址,默认为true (目前只有一个注册中心而且,当前就是注册中心..)
fetch-registry: false
#是否将允许自己注入注册中心,默认为true (目前只有一个注册中心而且,当前就是注册中心..自己注册自己?)
register-with-eureka: false #如果一个应用中有两个注册中心可以开启,达到集群注册中心的目的...
server:
enable-self-preservation: false #默认是ture 开启的
eviction-interval-timer-in-ms: 4000 #默认是0 从不剔除!
保护机制只要关闭就会报错!
而且只要服务失效就会移除…