⚫ 需要对分布式架构有一定等了解
⚫ 需要对Spring Boot有一定的了解
Spring Cloud是一系列框架的有序集合。 它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装 , 屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
单体架构也称之为单体系统或者是单体应用。就是一种把系统中所有的功能、模块耦合在一个应用中的架构方式。
特点 : 打包成一个独立的单元 jar包或者 war包 ;会以进程的方式运行
优点:项目易于管理、部署简单。
缺点:测试成本高、可伸缩性差、可靠性差、迭代困难、跨语言程度差、团队协作难
架构风格: 项目的一种设计模式
架构风格 | 举例 |
---|---|
客户端与服务端(C/S) | QQ, 微信等 |
基于组件模型的架构( 基于组件模型的架构 EJB ) | https://blog.csdn.net/lovemenghaibin/article/details/50666964 |
分层架构 (MVC) | OA系统,CMS内容管理系统等等 |
面向服务架构 (SOA) | 分布式项目-Ego电商项目等 |
MVC 架构
MVC(Model-View-Controller) : MVC架构就是一个单体架构。
代表技术: Struts2 、SpringMVC 、Spring 、Mybatis 等。
RPC 架构
RPC(Remote Procedure Call) :远程过调用。通过网络从计算机序上请求服务,而不需要了解底层网络技术的协议的架构方式。
代表技术:Thrift 、Hessian等
SOA(Service oriented Architecture): 面向服务架构
ESB(Enterparise Servce Bus): 企业服务总线,中介。主要是提供了一个于企业服务总线,
ESB 包含的功能如:负载均衡,流量控制, 加密处理, 服务的监控, 异常处理, 监控告急等。
代表技术: Mule 、WSO2
微服务架构
微服务就是一个轻量级的治理方案。
代表技术: SpringCloud、dubbo 等
诞生背景
业界对于可扩展的系统架构设计有一个朴素的理念,就是: 通过加机器就可以解决容量和可用性问题。(如果一台不行那就两台)。 这一理念在“云计算”概念疯狂流行的今天,得到了广泛的认可!对于一个规模迅速增长的系统而言,容量和性能问题当然是首当其冲的。但是随着时间的向前,系统规模的增长,除了面对性能与容量的问题外,还需要面对功能与模块数量上的增长带来的系统复杂性问题以及业务的变化带来的提供差异化服务问题。而许多系统,在架构设计时并未充分考虑到这些问题,导致系统的重构成为常态,从而影响业务交付能力,还浪费人力财力!对此,《可扩展的艺术》一书提出了一个更加系统的可扩展模型—— AKF可扩展立方 (Scalability Cube)。这个立方体中沿着三个坐标轴设置分别为:X、Y、Z。
坐标介绍
Y轴(功能 ) —— 关注应用中功能划分, 基于不同的业务拆分
X轴(水平扩展) —— 关注水平扩展,也就是”加机器解决问题”
Z轴(数据分区) —— 关注服务和数据的优先级划分,如按地域划分
Y 轴扩展会将庞大的整体应用拆分为多个服务。每个服务实现一组相关的功能,如订单管理、客户管理等。在工程上常见的方案是面向服务架构(SOA) 。比如对于一个电子商务平台,我们可以拆分成不同的服务,组成下面这样的架构:
但通过观察上图容易发现,当服务数量增多时,服务调用关系变得复杂。为系统添加一个新功能,要调用的服务数也变得不可控,由此引发了服务管理上的混乱。所以,一般情况下,需要采用服务注册的机制形成服务网关来进行服务治理。系统的架构将变成下图所示:
为了提升单个服务的可用性和容量, 对每一个服务进行X 轴扩展划分。
X 轴扩展与我们前面朴素理念是一致的,通过绝对平等地复制服务与数据,以解决容量和可用性的问题。其实就是将微服务运行多个实例,做集群加负载均衡的模式。
Z 轴扩展通常是指基于请求者或用户独特的需求,进行系统划分,并使得划分出来的子系统是相互隔离但又是完整的。以生产汽车的工厂来举例:福特公司为了发展在中国的业务,或者利用中国的廉价劳动力,在中国建立一个完整的子工厂,与美国工厂一样,负责完整的汽车生产。这就是一种Z 轴扩展。
工程领域常见的Z 轴扩展有以下两种方案
前后端分离原则,简单来讲就是前端和后端的代码分离,我们推荐的模式是最好采用物理分离的方式部署,进一步促使更彻底的分离。如果继续直接使用服务端模板技术,如:jsp,把java、js、html、css 都堆到一个页面里,稍微复杂一点的页面就无法维护了。
何为前后端分离?前后端本来不就分离么?这要从尴尬的jsp 讲起。分工精细化从来都是蛋糕做大的原则,多个领域工程师最好在不需要接触其他领域知识的情况下合作,才可能使效率越来越高,维护也会变得简单。jsp 的模板技术融合了html 和java 代码,使得传统MVC 开发中的前后端在这里如胶似漆,前端做好页面,后端转成模板,发现问题再找前端,前端又看不懂java 代码…前后端分离的目的就是将这尴尬局面打破。
这种分离方式有几个好处
- 前后端技术分离,可以由各自的专家来对各自的领域进行优化,这样前段的用户体验优化效果更好。
- 分离模式下,前后端交互界面更清晰,就剩下了接口模型,后端的接口简洁明了,更容易维护。
- 前端多渠道集成场景更容易实现,后端服务无需变更,采用统一的数据和模型,可以支持多个前端:例如:微信h5 前端、PC 前端、安卓前端、IOS 前端。
对于无状态服务,首先说一下什么是状态:如果一个数据需要被多个服务共享,才能完成一笔交易,那么这个数据被称为状态。进而依赖这个“状态”数据的服务被称为有状态服务,反之称为无状态服务。 那么这个无状态服务原则并不是说在微服务架构里就不允许存在状态,表达的真实意思是要把有状态的业务服务改变为无状态的计算类服务,那么状态数据也就相应的迁移到对应的“有状态数据服务”中。
场景说明:例如我们以前在本地内存中建立的数据缓存、Session缓存,到现在的微服务架构中就应该把这些数据迁移到分布式缓存中存储,让业务服务变成一个无状态的计算节点。迁移后,就可以做到按需动态伸缩,微服务应用在运行时动态增删节点,就不再需要考虑缓存数据如何同步的问题。
作为一个原则来讲本来应该是个“无状态通信原则”,在这里我们直接推荐一个实践优选的Restful 通信风格 ,因为他有很多好处:
- 无状态协议HTTP,具备先天优势,扩展能力很强。例如需要安全加密,有现成的成熟方案HTTPS即可。
- JSON 报文序列化,轻量简单,人与机器均可读,学习成本低,搜索引擎友好。
- 语言无关,各大热门语言都提供成熟的Restful API框架,相对其他的一些RPC框架生态更完善。
SpringCloud概念
是一个服务治理平台,是一系列框架的有序集合。包含了:服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等
与Dubbo的区别
Spring Cloud 是一个微服务框架,相比Dubbo 等RPC 框架, Spring Cloud 提供的全套的分布式系统解决方案。
Spring Cloud 对微服务基础框架Netflix 的多个开源组件进行了封装,同时又实现了和云端平台以及和Spring Boot 开发框架的集成。
Spring Cloud 为微服务架构开发涉及的配置管理,服务治理,熔断机制,智能路由,微代理,控制总线,一次性token,全局一致性锁,leader 选举,分布式session,集群状态管理等操作提供了一种简单的开发方式。
Spring Cloud 为开发者提供了快速构建分布式系统的工具,开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接。
SpringCloud的项目的位置
Sping Cloud 是Spring 的一个顶级项目与Spring Boot、Spring Data 位于同一位置。
Spring Cloud 包含了很多子项目,如:
虽然在对项目的技术选型时 ,性能是个比较好的评价指标 ,但不是唯一的评价的指标. 就拿上图来说,虽然Dobbo与 SpringCloud的性能比大约是3 :1 ,在性能上虽然优于SpringCloud , 但是在看具体使用时 ,还需要根据项目的功能进行综合的选型.
软件版本号:2.0.2.RELEASE
2:主版本号。当功能模块有较大更新或者整体架构发生变化时,主版本号会更新
0:次版本号。次版本表示只是局部的一些变动。
2:修改版本号。一般是bug 的修复或者是小的变动
RELEASE:希腊字母版本号。次版本号用户标注当前版本的软件处于哪个开发阶段
希腊字母版本号 | 作用 |
---|---|
Base | 设计阶段。只有相应的设计没有具体的功能实现。 |
Alpha | 软件的初级版本。存在较多的bug |
Bate | 表示相对alpha 有了很大的进步,消除了严重的bug,还存在一些潜在的bug。 |
Release | 该版本表示最终版。 |
设计的目的是为了更好的管理每个Spring Cloud 的子项目的清单。避免子的版本号与子项目的版本号混淆。
版本号单词的定义规则
采用伦敦的地铁站名称来作为版本号的命名,根据首字母排序,字母顺序靠后的版本号越大。
注意 : 在使用SpringCloud时 ,建议使用SR 版本(如下图),因为这样的版本是正式发布版 ,稳定且无重大bug
Spring Boot 是在Spring 的基础之上产生的(确切的说是在Spring4.0 的版本的基础之上),
其中“Boot”的意思就是“引导”,意在简化开发模式,是开发者能够快速的开发出基于Spring 的应用。Spring Boot 含有一个内嵌的web 容器。我们开发的web 应用不需要作为war包部署到web 容器中,而是作为一个jar 包,在启动时根据web 服务器的配置进行加载。
在项目中存在大量的xml 文件,配置相当繁琐
整合第三方框架时的配置问题
低效的开发效率与部署效率问题
Spring Boot 使配置简单
Spring Boot 使编码加单
Spring Boot 使部署简单
Spring Boot 使监控简单
注意 :
1.可能是Maven版本的问题导致pom.xml出现异常,但是运行不报错就可忽略
2.第一次导入SpringBoot项目(Maven)时,需要下载相关的jar 等待下载后即可使用!!!
修改内嵌容器的端口号
server.port=8888
自定义属性配置
msg=Hello World
@Value("${msg}")
private String msg;
配置变量引用
hello=szxy
msg=Hello World ${hello}
@Value("${msg}")
private String msg;
随机值配置
配置随机值
num=${random.int}
msg=Hello World ${num}
@Value("${msg}")
private String msg;
用处:配置随机值,在程序中如果有一些运算需要一个随机值,那么可以使用该方式来生成。
注意: 启动一次SpringBoot项目只生成一次。
配置随机启动端口
server.port=${random.int[1024,9999]}
用处:在SpringCloud 的微服务中,我们是不需要记录IP 与端口号的。让其随机生成就可以了。
yml 配置文件的语法
#配置SpringBoot的启动端口号
server:
port: 9999
#配置可以被引用的常量
msg:
ymlmsg: hello
othermsg: world
引用yml中的常量
@Value("${msg.ymlmsg}")
private String msg;
@Value("${msg.othermsg}")
private String othermsg;
yml 配置文件与properties 文件的区别
注 :在使用SpringBoot时 ,我们可以使用全局配置文件 application.properties,也可以使用 yml ,他们两个的作用都是定义全局属性 ,我们根据自己的喜好选择
1 导入相关的jar 包(使用SpringBoot项目时 ,自动导入)
2 添加logback.xml 配置文件
<configuration>
<property name="LOG_HOME" value="${catalina.base}/logs/" />
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
pattern>
layout>
appender>
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.logFileNamePattern>
<MaxHistory>30MaxHistory>
rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
pattern>
layout>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MBMaxFileSize>
triggeringPolicy>
appender>
<root level="info">
<appender-ref ref="Stdout" />
<appender-ref ref="RollingFile" />
root>
configuration>
日志级别 | 作用 |
---|---|
FATAL | 致命的, 表示非常严重的错误, 一般是系统错误 |
ERROR | 错误, 表示代码错误, 比较严重 |
WARN | 警告, 不影响程序的运行, 但是可能存在风险. |
INFO | 信息, 表示一个普通的输出信息 |
DEBUG | 调试, 表示程序员人为的一些调试信息 |
在不同的开发阶段需要不同的配置来运行项目,我们避免重复配置和打包,打包前配置多个配置文件,运行时使用参数指定加载的配置文件。
profile:代表的就是一个环境变量
语法结构:application-{profile}.properties
0 需求:
application-dev.properties 开发环境
application-test.properteis 测试环境
application-prod.properteis 生产环境
1 打包项目 , 放到相应的磁盘下(我放的是f盘 )
注意打包时可能出现如下图所示异常
解决方案
调整单元测试用例所在包路径 <= @SpringBootConfiguration标记的类和启动类
大意为: 测试SpringBoot应用时,不需要像Spring一样通过注解@ContextConfiguration(classes=…)或者@Configuration来指定需要加载的配置文件, @*Test注解会在没有配置的情况下自动寻找.
#搜寻算法是从单元测试类所在包向上层寻找,被@SpringBootApplication 或@SpringBootConfiguration 注解的class.
2 运行项目:
java -jar xxx.jar --spring.profiles.active={profile}
3 完成的命令:
java -jar springbootDemo-0.0.1-SNAPSHOT.jar --spring.profiles.active=test|dev|prod
相关注解 | 作用 |
---|---|
@SpringBootApplication | 代表是SpringBoot 的启动类。 |
@SpringBootConfiguration | 通过bean 对象来获取配置信息 |
@Configuration | 通过对bean 对象的操作替代spring 中xml 文件 |
@EnableAutoConfiguration | 完成一些初始化环境的配置。 |
@ComponentScan | 来完成spring 的组件扫描。替代之前我们在xml 文件中配置组件扫描的配置
|
@RestController: | 1、表示一个Controller。 2、表示当前这个Controller 下的所有的方法都会以json 格式的数据响应。 |
异常处理(见第二部分)
https://blog.csdn.net/qq_43371556/article/details/100108538
在pom 文件中添加Actuator 的坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
修改配置文件
2.0.0版本一下需要在全局配置文件中设置关闭安全限制
management.security.enabled=false
2.0.0版本以上需要修改默认显示的信息,使显示所有
management.endpoints.web.exposure.include=*
通过访问url获取信息。
例如:http://localhost:8080/actuator
服务端其实也是一个SpringBoot 项目.
官网:https://github.com/codecentric/spring-boot-admin
官网有详细的安装流程
添加依赖
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-serverartifactId>
<version>2.1.6version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
注意:
使用的SpringBoot的版本最好是和spring-boot-admin管理工具的版本一样!!!,否则很大可能报错
在启动类上添加@EnableAdminServer注解
@SpringBootApplication
@EnableAdminServer
public class SpringBootAdminApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAdminApplication.class, args);
}
}
添加依赖:
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-clientartifactId>
<version>2.1.6version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
注意:
1.使用的SpringBoot的版本最好是和spring-boot-admin管理工具的版本一样!!!,否则很大可能报错,客户端
2.客户端使用的 spring-boot-security需要在指定的仓库才能下载!!!所以将客户端的pom文件放在下面
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
pluginRepository>
pluginRepositories>
修改配置文件:
#2.0.0版本以后需要设置显示所有
management.endpoints.web.exposure.include=*
#设置SpringBootAdmin监控服务的地址(client端用)
spring.boot.admin.client.url=http://localhost:9090
#设置安全管理的用户名密码
spring.security.user.name=admin
spring.security.user.password=admin
添加一个类使执行器端点可访问
@Configuration
public class SecurityPermitAllConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll()
.and().csrf().disable();
}
}
安装环境依赖
yum -y install make gc-c gcc-c++ kernel-devel m4 ncurses-devel openssl-devel
yum -y install perl
上传安装包
otp_src_18.3.tar.gz
rabbitmq-server-generic-unix-3.6.1.tar.xz
软件和客户端安装包分享 https://pan.baidu.com/s/1T1RwvZ_Rj3f_ui2i7Dsnzw
解压缩otp_src_18.3.tar.gz
tar -xvf otp_src_18.3.tar.gz
建立erlang安装环境
创建目录mkdir /usr/local/erlang
在解压目录下运行
./configure --prefix=/usr/local/erlang --with-ssl -enable-threads -enable-smmp-support -enable-kernel-poll --enable-hipe --without-javac
编译安装erlang
在解压目录运行
make && make install
配置erlang的环境变量
vim /etc/profile
ERLANG_HOME=/usr/local/erlang
PATH=$ERLANG_HOME/bin:$PATH
export ERLANG_HOME
export PATH
特别注意: 涉及修改环境变量 ,需要重启虚拟机或者重新加载修改环境变量的这个文件,重新加载环境变量文件命令如下 source /etc/profile
解压rabbitmq
在压缩包所在目录下, 先执行yum -y install xz
再执行xz -d rabbitmq-server-generic-unix-3.6.1.tar.xz
最后执行tar -xvf rabbitmq-server-generic-unix-3.6.1.tar
拷贝解压后的文件夹到/usr/local/rabbitmq
[root@bogon Downloads]# cp rabbitmq_server-3.6.1/ /usr/local/rabbitmq/ -rf
运行rabbitmq
进入rabbitmq的sbin目录
./rabbitmq-server
./rabbitmq-server -detached
./rabbitmqctl stop
ps -ef|grep rabbitmq
kill -9 ****
#创建用户(账号mquser密码mquser),最好手动输入
./rabbitmqctl add_user mquser mquser
#设置管理员权限
./rabbitmqctl set_user_tags mquser administrator
#查看用户和权限
./rabbitmqctl list_users
#给用户访问虚拟机的权限
./rabbitmqctl set_permissions -p / mquser '.*' '.*' '.*'
开放端口5672和15672端口( 或者使用时关闭防火墙 )
#编辑防火墙配置文件
vim /etc/sysconfig/iptables
# 开放5672(RabbitMQ的端口号). 15672(RabbitMQ客户端的端口号)
-A INPUT -m state --state NEW -m tcp -p tcp --dport 5672 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 15672 -j ACCEPT
启动mq的管理ui
./rabbitmq-plugins enable rabbitmq_management
访问rabbitmq的管理员UI
http://服务器IP地址:15672/
注: 如果访问不到管理界面配置文件没有生效,最好是重启防火墙 ,让配置文件生效
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
消息生产者,就是投递消息的程序。
消息消费者,就是接受消息的程序。
队列就像存放了商品的仓库或者商店,是生产商品的工厂和购买商品的用户之间的中转站
在 rabbitMQ 中,信息流从应用程序出发,来到 Rabbitmq 的队列,所有信息可以只 存储在一个队列中。
队列可以存储很多信息,因为它基本上是一个无限制的缓冲区,前提是你的机器有足够的存储空间。
多个生产者可以将消息发送到同一个队列中,多个消息者也可以只从同一个队列接收数据。
pom文件头部报错可能是eclipse或jdk版本问题 ,不影响程序运行, 可忽略
创建jar项目,修改pom文件
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.6.RELEASEversion>
<relativePath />
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
全局配置文件
#项目名(随便起)/ RabbitMQ的ip/端口
spring.application.name=springcloud-rabbitmq
spring.rabbitmq.host=192.168.179.136
spring.rabbitmq.port=5672
#RabbitMQ的用户名与密码
spring.rabbitmq.username=mquser
spring.rabbitmq.password=mquser
创建消息队列,注意所导的包
在这里new一个队列后 ,发送者和使用者必须通过这个名字才能对改消息进行发送和接收!!!
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 1. 创建消息队列
* @Configuration即用来代替Spring配置文件的,
* 它就是一个@Component组件,接收一个value值也就是bean的名字,value可以不填。
* @author chy
*
*/
@Configuration
public class QueueConfig {
/**
* 创建队列
*
* @return
*/
@Bean
public Queue createQueue() {
return new Queue("hello-queue-队列名称保持一致");
}
}
消息发送者类
/**
* 2. 创建消息发送者
* @author chy
*
*/
@Component
public class Sender {
@Autowired
private AmqpTemplate rabbitAmqpTemplate;
/*
* 发送消息的方法
*/
public void send(String msg) {
// 向消息队列发送消息
// 参数一:队列的名称。
// 参数二:消息
this.rabbitAmqpTemplate.convertAndSend("hello-queue-队列名称保持一致", msg);
}
}
消息接收者类
/**
* 3. 消息接收者
*
* @author chy
*
*/
@Component
public class Receiver {
/**
* 接收消息的方法。采用消息队列监听机制
* @param msg
*/
@RabbitListener(queues = "hello-queue-队列名称保持一致")
public void process(String msg) {
System.out.println("receiver: " + msg);
}
}
启动类
/**
* 4. 启动类
* @author chy
*
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class QueueTest {
@Autowired
private Sender sender;
/*
* 测试消息队列
*/
@Test
public void test1() {
this.sender.send("Meeeage: Hello RabbitMQ");
}
}
组件/说明 |
---|
1. Message 消息 |
消息是不具名的,它由消息头消息体组成。消息体是不透明的,而消息头则由一系列可选属性组成,这些属性包括:routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出消息可能持久性存储)等。 |
2. Publisher 消息的生产者 |
也是一个向交换器发布消息的客户端应用程序。 |
3. Consumer 消息的消费者 |
表示一个从消息队列中取得消息的客户端应用程序。 |
4. Exchange 交换器 |
用来接收生产者发送的消息并将这些消息路由给服务器中的队列。 三种常用的交换器类型 1. direct(发布与订阅 完全匹配) 2. fanout(广播) 3. topic(主题,规则匹配) |
5. Binding 绑定 |
用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息 队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。 |
6. Queue 消息队列 |
用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一 个消息可投入一个或多个队列。消息一直在队列里面,等待消费者链接到这个队列将其取走。 |
7. Routing-key 路由键 |
RabbitMQ 决定消息该投递到哪个队列的规则。 队列通过路由键绑定到交换器。 消息发送到 MQ 服务器时,消息将拥有一个路由键,即便是空的,RabbitMQ 也会将其 和绑定使用的路由键进行匹配。 如果相匹配,消息将会投递到该队列。 如果不匹配,消息将会进入黑洞。 |
8. Connection 链接 |
指 rabbit 服务器和服务建立的 TCP 链接。 |
9. Channel 信道 |
1. Channel 中文叫做信道,是 TCP 里面的虚拟链接。例如:电缆相当于 TCP,信道是 一个独立光纤束,一条 TCP 连接上创建多条信道是没有问题的。 2. TCP 一旦打开,就会创建 AMQP 信道。 3. 无论是发布消息、接收消息、订阅队列,这些动作都是通过信道完成的。 |
10. Virtual Host虚拟主机 |
表示一批交换器,消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在链接时指定, RabbitMQ 默认的 vhost 是"/" |
11. Borker |
表示消息队列服务器实体。 |
12. 交换器和队列的关系 |
13. 交换器是通过路由键和队列绑定在一起的,如果消息拥有的路由键跟队列和交换器的 路由键匹配,那么消息就会被路由到该绑定的队列中。也就是说,消息到队列的过程中,消息首先会经过交换器,接下来交换器在通过路由 键匹配分发消息到具体的队列中。路由键可以理解为匹配的规则。 |
pom.xml
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.21.RELEASEversion>
<relativePath />
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.amqpgroupId>
<artifactId>spring-rabbitartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-clientartifactId>
<version>1.5.7version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
1.配置文件
#项目名/ RabbitMQ的ip/端口
#spring.application.name=springcloud-provider
#spring.rabbitmq.host=192.168.179.136
#spring.rabbitmq.port=5672
#RabbitMQ的用户名与密码
#spring.rabbitmq.username=mquser
#spring.rabbitmq.password=mquser
# 交换器
mq.config.exchange=log-direct
# 队列
mq.config.queue.info=log.info
# 路由键
mq.config.queue.info.routing.key=log.info.routing.key
# 队列
mq.config.queue.error=log.error
# 路由键
mq.config.queue.error.routing.key=log.error.routing.key
2.发送者类(两个,作用相同)
/**
* 消息发送者-发送错误消息
*
*/
@Component
public class ErrorSender {
@Autowired
private RabbitTemplate rabbiteTemplate;
@Value("${mq.config.exchange}")
private String exchange;
@Value("${mq.config.queue.error.routing.key}")
private String key;
/**
* 发送消息
* @param msg
*/
public void sendMsg(String msg) {
/**
* 第一个参数表示 交换器的名称
* 第二个参数表示 路由键
* 第三个参数表示 消息,将要发送的消息
*/
this.rabbiteTemplate.convertAndSend(exchange,key,msg);
}
}
/**
* 消息发送者-发送info消息
*
*/
@Component
public class InfoSender {
@Autowired
private RabbitTemplate rabbiteTemplate;
@Value("${mq.config.exchange}")
private String exchange;
@Value("${mq.config.queue.info.routing.key}")
private String key;
/**
* 发送消息
* @param msg
*/
public void sendMsg(String msg) {
/**
* 第一个参数表示 交换器的名称
* 第二个参数表示 路由键
* 第三个参数表示 消息,将要发送的消息
*/
this.rabbiteTemplate.convertAndSend(exchange,key,msg);
}
}
3.启动类
注意
1.使用RabbitMQ启动类需要加@EnableRabbit注解!!!
2.在这里提供者的启动类需要配合测试类使用
@SpringBootApplication
@EnableRabbit
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
4.测试类
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class QueueTest {
@Autowired
private ErrorSender errorSender;
@Autowired
private InfoSender infoSender;
@Test
public void testError() throws Exception{
// TODO
int flag = 0;
while(true) {
Thread.sleep(2000);
this.errorSender.sendMsg("Hello World RabbitMq"+(flag++));
}
}
@Test
public void testInfo() throws Exception{
// TODO
int flag = 0;
while(true) {
Thread.sleep(2000);
this.infoSender.sendMsg("Hello World RabbitMq"+(flag++));
}
}
}
消息消费者
1.配置文件
#项目名/ RabbitMQ的ip/端口
spring.application.name=springcloud-consumer
spring.rabbitmq.host=192.168.179.136
spring.rabbitmq.port=5672
#RabbitMQ的用户名与密码
spring.rabbitmq.username=mquser
spring.rabbitmq.password=mquser
# 交换器
mq.config.exchange=log-direct
# 队列
mq.config.queue.info=log.info
# 路由键
mq.config.queue.info.routing.key=log.info.routing.key
# 队列
mq.config.queue.error=log.error
# 路由键
mq.config.queue.error.routing.key=log.error.routing.key
# 解决 ACK 反馈
#开启重试
spring.rabbitmq.listener.retry.enabled=true
#重试次数,默认为 3 次
spring.rabbitmq.listener.retry.max-attempts=5
2.接收者类(两个,结构相同)
重点掌握相关注解的使用
@RabbitHandler用于令消费者的相关方法监听服务队列
/**
* 消息接收者 消息接收者
*
* @RabbitListener bindings:绑定队列
*
* @QueueBinding value:绑定队列的名称
* exchange:配置交换器
* key:路由键
* @Queue value:配置队列名称
* autoDelete:是否是一个可删除的临时队列
*
* @Exchange value:为交换器起个名称
* type:指定具体的交换器类型
*/
@Component
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.DIRECT),
value = @Queue(value = "${mq.config.queue.error}", autoDelete = "false"),
key = "${mq.config.queue.error.routing.key}")
)
public class ErrorReceiver {
/**
* 监听指定的消息队列
*
* @param msg
* @throws Exception
*/
@RabbitHandler
public void receiveMsg(String msg) throws Exception {
System.out.println("Error------"+msg);
// 手动抛出异常
//throw new Exception();
}
}
/**
* 消息接收者 消息接收者
*
* @RabbitListener bindings:绑定队列
*
* @QueueBinding value:绑定队列的名称
* exchange:配置交换器
* @Queue value:配置队列名称
* autoDelete:是否是一个可删除的临时队列
*
* @Exchange value:为交换器起个名称
* type:指定具体的交换器类型
*/
@Component
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.DIRECT),
value = @Queue(value = "${mq.config.queue.info}", autoDelete = "true"),
key = "${mq.config.queue.info.routing.key}")
)
public class InfoReceiver {
/**
* 监听指定的消息队列
*
* @param msg
*/
@RabbitHandler
public void receiveMsg(String msg) {
System.out.println("Info------"+msg);
}
}
3.启动类(启动消费者)
注意使用RabbitMQ启动类需要加@EnableRabbit注解!!!
@SpringBootApplication
@EnableRabbit
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
##项目名/ RabbitMQ的ip/端口
#spring.application.name=springcloud-provider
#spring.rabbitmq.host=192.168.179.136
#spring.rabbitmq.port=5672
##RabbitMQ的用户名与密码
#spring.rabbitmq.username=mquser
#spring.rabbitmq.password=mquser
# 交换器
mq.config.exchange=log.topic
2.发送者类(3个)
@Component
public class OrderSender {
@Autowired
private RabbitTemplate rabbiteTemplate;
@Value("${mq.config.exchange}")
private String exchange;
/**
* 发送消息
* @param msg
*/
public void sendMsg(String msg) {
/**
* 第一个参数表示 交换器的名称
* 第二个参数表示 路由键
* 第三个参数表示 消息,将要发送的消息
*/
this.rabbiteTemplate.convertAndSend(exchange,"order.log.debug","order.log.Debgu"+msg);
this.rabbiteTemplate.convertAndSend(exchange,"order.log.info","order.log.Info"+msg);
this.rabbiteTemplate.convertAndSend(exchange,"order.log.warn","order.log.Warn"+msg);
this.rabbiteTemplate.convertAndSend(exchange,"order.log.error","order.log.Error"+msg);
}
}
@Component
public class ProductSender {
@Autowired
private RabbitTemplate rabbiteTemplate;
@Value("${mq.config.exchange}")
private String exchange;
/**
* 发送消息
* @param msg
*/
public void sendMsg(String msg) {
/**
* 第一个参数表示 交换器的名称
* 第二个参数表示 路由键
* 第三个参数表示 消息,将要发送的消息
*/
this.rabbiteTemplate.convertAndSend(exchange,"product.log.debug","product.log.Debgu"+msg);
this.rabbiteTemplate.convertAndSend(exchange,"product.log.info","product.log.Info"+msg);
this.rabbiteTemplate.convertAndSend(exchange,"product.log.warn","product.log.Warn"+msg);
this.rabbiteTemplate.convertAndSend(exchange,"product.log.error","product.log.Error"+msg);
}
}
@Component
public class UserSender {
@Autowired
private RabbitTemplate rabbiteTemplate;
@Value("${mq.config.exchange}")
private String exchange;
/**
* 发送消息
* @param msg
*/
public void sendMsg(String msg) {
/**
* 第一个参数表示 交换器的名称
* 第二个参数表示 路由键
* 第三个参数表示 消息,将要发送的消息
*/
this.rabbiteTemplate.convertAndSend(exchange,"user.log.debug","user.log.Debgu"+msg);
this.rabbiteTemplate.convertAndSend(exchange,"user.log.info","user.log.Info"+msg);
this.rabbiteTemplate.convertAndSend(exchange,"user.log.warn","user.log.Warn"+msg);
this.rabbiteTemplate.convertAndSend(exchange,"user.log.error","user.log.Error"+msg);
}
}
3.启动类
@SpringBootApplication
@EnableRabbit
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
4.测试类(生产者执行的入口,会默认启动上面的启动类)
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class QueueTest {
@Autowired
private OrderSender orderSender;
@Autowired
private ProductSender productSender;
@Autowired
private UserSender userSender;
@Test
public void test() throws Exception{
this.orderSender.sendMsg("UserSender----");
this.productSender.sendMsg("Producer----");
this.userSender.sendMsg("UserSnder------");
}
}
消息消费者
1.配置文件
##项目名/ RabbitMQ的ip/端口
#spring.application.name=springcloud-consumer
#spring.rabbitmq.host=192.168.179.136
#spring.rabbitmq.port=5672
##RabbitMQ的用户名与密码
#spring.rabbitmq.username=mquser
#spring.rabbitmq.password=mquser
#设置交换器的名称
mq.config.exchange=log.topic
#info 队列名称
mq.config.queue.info=log.info
#error 队列名称
mq.config.queue.error=log.error
#log 队列名称
mq.config.queue.logs=log.all
2.接收者类(3个)
/**
* 消息接收者
*
* @RabbitListener bindings:绑定队列
*
* @QueueBinding value:绑定队列的名称
* exchange:配置交换器
* key:路由键 :接收以el表达式内部代码格式结尾的消息
* @Queue value:配置队列名称
* autoDelete:是否是一个可删除的临时队列
*
* @Exchange value:为交换器起个名称
* type:指定具体的交换器类型
*
*/
@Component
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.TOPIC),
value = @Queue(value = "${mq.config.queue.error}", autoDelete = "true"),
key = "*.log.error")
)
public class ErrorReceiver {
/**
* 监听指定的消息队列
*
* @param msg
* @throws Exception
*/
@RabbitHandler
public void Receiver(String msg){
System.out.println("......Error........receiver:"+msg);
}
}
@Component
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.TOPIC),
value = @Queue(value = "${mq.config.queue.info}", autoDelete = "true"),
key = "*.log.info")
)
public class InfoReceiver {
/**
* 监听指定的消息队列
*
* @param msg
*/
@RabbitHandler
public void Receiver(String msg) {
System.out.println("......Info........receiver:"+msg);
}
}
@Component
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.TOPIC),
value = @Queue(value = "${mq.config.queue.logs}", autoDelete = "true"),
key = "*.log.*")
)
public class LogsReceiver {
/**
* 监听指定的消息队列
*
* @param msg
* @throws Exception
*/
@RabbitHandler
public void Receiver(String msg) throws Exception {
System.out.println("......All........receiver:"+msg);
}
}
3.启动类
@SpringBootApplication
@EnableRabbit
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
1.配置文件
#项目名/ RabbitMQ的ip/端口
#spring.application.name=springcloud-provider
#spring.rabbitmq.host=192.168.179.136
#spring.rabbitmq.port=5672
#RabbitMQ的用户名与密码
#spring.rabbitmq.username=mquser
#spring.rabbitmq.password=mquser
# 交换器
mq.config.exchange=order.fanout
2.发送类
@Component
public class Sender {
@Autowired
private RabbitTemplate rabbiteTemplate;
@Value("${mq.config.exchange}")
private String exchange;
/**
* 发送消息
* @param msg
*/
public void sendMsg(String msg) {
/**
* 第一个参数表示 交换器的名称
* 第二个参数表示 路由键
* 第三个参数表示 消息,将要发送的消息
*/
this.rabbiteTemplate.convertAndSend(exchange,"",msg);
}
}
消息消费者
1.配置文件
#项目名/ RabbitMQ的ip/端口
#spring.application.name=springcloud-consumer
#spring.rabbitmq.host=192.168.179.136
#spring.rabbitmq.port=5672
#RabbitMQ的用户名与密码
#spring.rabbitmq.username=mquser
#spring.rabbitmq.password=mquser
# 交换器
mq.config.exchange=order.fanout
# 设置短信服务队列名称
mq.config.queue.sms=order.sms
# push服务队列名称
mq.config.queue.push=order.push
2.接收者类(两个)
/**
* 消息接收者 消息接收者
* @RabbitListener bindings:绑定队列
* @QueueBinding value:绑定队列的名称
* exchange:配置交换器
* @Queue value:配置队列名称
* autoDelete:是否是一个可删除的临时队列
*
* @Exchange value:为交换器起个名称
* type:指定具体的交换器类型
*/
@Component
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.FANOUT),
value = @Queue(value = "${mq.config.queue.push}", autoDelete = "true")
)
)
public class PushReceiver {
/**
* 监听指定的消息队列
*
* @param msg
*/
@RabbitHandler
public void receiveMsg(String msg) {
System.out.println("push消费者消费信息:------"+msg);
}
}
@Component
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.FANOUT),
value = @Queue(value = "${mq.config.queue.sms}", autoDelete = "false")
)
)
public class SmsReceiver {
/**
* 监听指定的消息队列
*
* @param msg
* @throws Exception
*/
@RabbitHandler
public void receiveMsg(String msg) throws Exception {
System.out.println("sms消费者消费信息------"+msg);
// 手动抛出异常
//throw new Exception();
}
}
3.启动类(启动消费者项目)
@SpringBootApplication
@EnableRabbit
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
在Fanout交换器演示案例中添加如下代码,实现松耦合功能
1.在消息消费者的配置文件中
# 新增红包队列, 作为松耦合实例
mq.config.queue.red=order.red
@Component
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.FANOUT),
value = @Queue(value = "${mq.config.queue.red}", autoDelete = "true")
)
)
public class RedReceiver {
/**
* 监听指定的消息队列
*
* @param msg
* @throws Exception
*/
@RabbitHandler
public void receiveMsg(String msg) throws Exception {
System.out.println("我自愿给chy发送888红包------"+msg);
}
}
通过上述案例可知 ,对新队列的添加不仅实现了松耦合的功能 ,而且实现开闭原则 ,即对拓展开放, 对修改关闭
消息的可靠性是RabbitMQ 的一大特色,那么RabbitMQ 是如何保证消息可靠性的呢——消息持久化。
autoDelete 属性
@Queue: 当所有消费客户端连接断开后,是否自动删除
队列true:删除false:不删除
设置删除 ,会将系统宕机时发送给消息队列的消息删除 ; 反之则不会删除 ,待重启后还能读取消息队列中的消息
@Exchange:当所有绑定队列都不在使用时,是否自动
删除交换器true:删除false:不删除
定位到消息消费者项目的一个接收者类中
@Component
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.DIRECT),
value = @Queue(value = "${mq.config.queue.error}", autoDelete = "false"),
key = "${mq.config.queue.error.routing.key}")
)
public class ErrorReceiver {
@RabbitHandler
public void receiveMsg(String msg) throws Exception {
System.out.println("Error------"+msg);
// 手动抛出异常
throw new Exception();
}
}
Features中带有AD表示设置了自动删除
关闭自动删除 ,项目宕机 ,仍会将消息发送到队列中 ,可通过Ready或total查看
重启后, 读取消息队列中保存的消息
解决方法
#开启重试
spring.rabbitmq.listener.simple.retry.enabled=true
#最大重试次数
spring.rabbitmq.listener.simple.retry. max-attempts=5
服务注册中心介绍
服务注册中心是服务实现服务化管理的核心组件,类似于目录服务的作用,主要用来存储服务信息,譬如提供者 url 串、路由信息等。服务注册中心是SOA 架构中最基础的设施之一。
服务注册中心的作用
常见的注册中心有哪些
服务注册中心解决了什么问题
什么是 Eureka 注册中心
Eureka 是 Netflix 开发的服务发现组件,本身是一个基于 REST 的服务。Spring Cloud 将它集成在其子项目 spring-cloud-netflix 中,以实现 Spring Cloud 的服务注册于发现,同时 还提供了负载均衡、故障转移等能力。
Eureka 注册中心三种角色
角色 | 作用 |
---|---|
Eureka Server | 通过 Register、Get、Renew 等接口提供服务的注册和发现。 |
Application Service (Service Provider) | 服务提供方 , 把自身的服务实例注册到 Eureka Server 中 |
Application Client (Service Consumer) | 服务调用方 , 通过 Eureka Server 获取服务列表,消费服务。 |
服务注册中心是服务实现服务化管理的核心组件,类似于目录服务的作用,主要用来存储服务信息,譬如提供者 url 串、路由信息等。服务注册中心是SOA 架构中最基础的设施之一。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
@SpringBootApplication
@EnableEurekaServer
public class SpringCloudEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaApplication.class, args);
}
}
server:
port: 8888
spring:
application:
name: Eureka-Server
eureka:
client:
register-with-eureka: false #是否将自己注册到 Eureka-Server 中,默认的为 true
fetch-registry: false #是否冲 Eureka-Server 中获取服务注册信息,默认为 true
创建项目,同上
配置文件 ,在上面基础上额外添加
#设置 eureka 实例名称,与配置文件的变量为主
eureka.instance.hostname=服务名
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://ip+port/eu reka/
在搭建 Eureka 集群时,需要添加多个配置文件,并且使用 SpringBoot 的多环境配置方式。集群中需要多少节点就添加多少个配置文件。
配置节点信息
application-eureka1.properties(properties可以和yml文件相互转换)
spring.application.name=eureka-server
server.port=8761
#设置eureka 实例名称,与配置文件的变量为主
eureka.instance.hostname=eureka1
#设置服务注册中心地址,指向另一个注册中心,如果是两个以上,只需要在下面加上逗号 ,然后加上完整连接即可
eureka.client.serviceUrl.defaultZone=http://admin:admin@eureka2:8761/eureka/
spring.security.user.name=admin
spring.security.user.password=admin
#Eureka注册中心
#是否将自己注册到 Eureka-Server 中,默认的为 true,使用单机的时候需要改为false
#eureka.client.register-with-eureka=false
#是否从 Eureka-Server 中获取服务注册信息,默认为 true
eureka.client.fetch-registry=false
application-eureka2.properties
spring.application.name=eureka-server
server.port=8761
#设置eureka 实例名称,与配置文件的变量为主
eureka.instance.hostname=eureka2
#设置服务注册中心地址,指向另一个注册中心,如果是两个以上,只需要在下面加上逗号 ,然后加上完整连接即可
eureka.client.serviceUrl.defaultZone=http://admin:admin@eureka1:8761/eureka/
spring.security.user.name=admin
spring.security.user.password=admin
#Eureka注册中心
#是否将自己注册到 Eureka-Server 中,默认的为 true
#eureka.client.register-with-eureka=false
#是否从 Eureka-Server 中获取服务注册信息,默认为 true
eureka.client.fetch-registry=false
添加日志
将logback.xml放入resources目录下
<configuration>
<property name="LOG_HOME" value="${catalina.base}/logs/" />
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
pattern>
layout>
appender>
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.logFileNamePattern>
<MaxHistory>30MaxHistory>
rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
pattern>
layout>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MBMaxFileSize>
triggeringPolicy>
appender>
<root level="info">
<appender-ref ref="Stdout" />
<appender-ref ref="RollingFile" />
root>
configuration>
部署项目
使用maven打包项目, 将项目上传到linux下 , 编写并使用脚本启动程序 server.sh
#!/bin/bash
cd `dirname $0`
CUR_SHELL_DIR=`pwd`
CUR_SHELL_NAME=`basename ${BASH_SOURCE}`
JAR_NAME="项目名称"
JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME
#JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m"
JAVA_MEM_OPTS=""
#配置集群时使用
SPRING_PROFILES_ACTIV="-Dspring.profiles.active=配置文件变量名称"
#配置单机Eureka使用
#SPRING_PROFILES_ACTIV=""
LOG_DIR=$CUR_SHELL_DIR/logs
LOG_PATH=$LOG_DIR/${JAR_NAME%..log
echo_help()
{
echo -e "syntax: sh $CUR_SHELL_NAME start|stop"
}
if [ -z $1 ];then
echo_help
exit 1
fi
if [ ! -d "$LOG_DIR" ];then
mkdir "$LOG_DIR"
fi
if [ ! -f "$LOG_PATH" ];then
touch "$LOG_DIR"
fi
if [ "$1" == "start" ];then
# check server
PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`
if [ -n "$PIDS" ]; then
echo -e "ERROR: The $JAR_NAME already started and the PID is ${PIDS}."
exit 1
fi
echo "Starting the $JAR_NAME..."
# start
nohup java $JAVA_MEM_OPTS -jar $SPRING_PROFILES_ACTIV $JAR_PATH >> $LOG_PATH 2>&1 &
COUNT=0
while [ $COUNT -lt 1 ]; do
sleep 1
COUNT=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l`
if [ $COUNT -gt 0 ]; then
break
fi
done
PIDS=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'`
echo "${JAR_NAME} Started and the PID is ${PIDS}."
echo "You can check the log file in ${LOG_PATH} for details."
elif [ "$1" == "stop" ];then
PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`
if [ -z "$PIDS" ]; then
echo "ERROR:The $JAR_NAME does not started!"
exit 1
fi
echo -e "Stopping the $JAR_NAME..."
for PID in $PIDS; do
kill $PID > /dev/null 2>&1
done
COUNT=0
while [ $COUNT -lt 1 ]; do
sleep 1
COUNT=1
for PID in $PIDS ; do
PID_EXIST=`ps --no-heading -p $PID`
if [ -n "$PID_EXIST" ]; then
COUNT=0
break
fi
done
done
echo -e "${JAR_NAME} Stopped and the PID is ${PIDS}."
else
echo_help
exit 1
fi
注意:
windows下文件格式可能在linux下无法使用,使用notepad++转换成unix:编辑–>文档格式转换–>转为unix。
赋予脚本执行权限 chmod +R 755 server.sh
启动脚本 server.sh -start
关闭脚本 server.sh -stop
查看日志:在catalina/logs/server.log中
测试访问http://192.168.179.136:8761
http://192.168.179.137:8761
创建jar项目
修改pom文件
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
@SpringBootApplication
@EnableEurekaClient
public class SpringCloudEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaApplication.class, args);
}
}
server:
port: 8081
spring:
application:
name: Eureka-Provider
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
@RestController
public class UserController {
@RequestMapping("/user")
public List<User> user() {
List<User> list=new ArrayList<User>();
list.add(new User(1, "张三", 21));
list.add(new User(2, "李四", 22));
list.add(new User(3, "王五", 23));
list.add(new User(4, "赵六", 24));
return list;
}
}
创建jar项目
修改pom文件
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Greenwich.SR2spring-cloud.version>
<maven-jar-plugin.version>2.6maven-jar-plugin.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
@SpringBootApplication
@EnableEurekaClient
public class SpringCloudEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaApplication.class, args);
}
}
server:
port: 8091
spring:
application:
name: Eureka-Consumer
eureka:
client:
serviceUrl:
defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
@Service
public class UserService {
@Autowired
//ribbon 负载均衡器
private LoadBalancerClient balancerClient;
public List<User> getUsers(){
//选择调用的服务的名称
//ServiceInstance 封装了服务的基本信息,如 IP,端口
ServiceInstance si = this.balancerClient.choose("Eureka-Provider");
String url="http://"+si.getHost()+":"+si.getPort()+"/user";
//springMVC RestTemplate
//发送http请求
RestTemplate restTemplate=new RestTemplate();
//参数化类型引用
ParameterizedTypeReference<List<User>> responseType=new ParameterizedTypeReference<List<User>>() { };
//ResponseEntity:封装了返回值信息 exchange发送请求 路径 方式 参数
ResponseEntity<List<User>> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, responseType);
//获取响应体
List<User> list=responseEntity.getBody();
return list;
}
}
在consumer端访问http://localhost:8091/consumer.
会获取到provider的控制层产生的数据。
1 Eureka 架构图
Register(服务注册):把自己的 IP 和端口注册给 Eureka。
Renew(服务续约):发送心跳包,每 30 秒发送一次。告诉 Eureka 自己还活着。
Cancel(服务下线):当 provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除。防止consumer 调用到不存在的服务。
Get Registry(获取服务注册列表):获取其他服务列表。
Replicate(集群中数据同步):eureka 集群中的数据复制与同步。
Make Remote Call(远程调用):完成服务的远程调用。
1. 什么是 CAP 原则
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。
CAP 由 Eric Brewer 在 2000 年 PODC 会议上提出。该猜想在提出两年后被证明成 立,成为我们熟知的 CAP 定理
cap定理研究策略
2. Zookeeper 与 Eureka 的区别
3 如何关闭自我保护
修改 Eureka Server 配置文件
#关闭自我保护:true 为开启自我保护,false 为关闭自我保护
eureka.server.enableSelfPreservation=false
#清理间隔(单位:毫秒,默认是60*1000)
eureka.server.eviction.interval-timer-in-ms=60000
4 如何优雅停服
不需要在 Eureka Server 中配置关闭自我保护
需要在服务中添加 actuator.jar 包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
修改配置文件
#启用 shutdown
endpoints.shutdown.enabled=true
#禁用密码验证
endpoints.shutdown.sensitive=false
#2.0版本以上
management.endpoints.web.exposure.include: "*"
使用httpclient或者curl发送请求
http://ip:port/acyuator/shutdown
(curl:使用windows的cmd或者linux的终端:curl -X POST http://ip:port/shutdown)
1. 在 Eureka Server 中添加 security 包
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
2.修改 Eureka Server 配置文件( 添加账户密码 )
spring.application.name=Eureka-Server
server.port=8080
spring.security.basic.enabled=true
spring.security.user.name=admin
spring.security.user.password=admin
eureka.client.serviceUrl.defaultZone=http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
3.添加配置类 关闭csrf防护(2.0以上)
@Configuration
public class Config extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
super.configure(http);
}
}
4. 修改微服务的配置文件添加访问注册中心的用户名与密码
用户名 admin
密码 admin
eureka:
client:
serviceUrl:
defaultZone: http://admin:[email protected]:8080/eureka,http://admin:[email protected]:8080/eureka
传送门时间到~~~
SpringCloud高级阶段上 传送门
第一章 负载均衡Ribbon
第二章 声明式调用Feign
第三章 服务容错保护Hystrix
SpringCloud高级阶段中 传送门
第四章 如何设计微服务
第五章 服务网关Zuul
第六章 分布式配置中心
SpringCloud高级阶段下 传送门
第七章 消息总线Bus
第八章 消息驱动Stream
第九章 分布式服务跟踪Sleuth