目录
1、spring基础配置
2、微服务基础知识
3、eureka基础配置
eureka-server端的配置文件基本配置
eureka-client端的配置文件基本配置
4、Ribbon基础
5、Feign基础
Lombok 中常用的四个注解:
@Data:作用于类上,是以下注解的集合:@ToString @EqualsAndHashCode @Getter @Setter @RequiredArgsConstructor
@NoArgsConstructor:生成无参构造器
@AllArgsConstructor:生成全参构造器
@NonNull:作用于成员变量和参数中,标识不能为空
=============================================================================
//spring整合mybatis常见配置=============================================================
configuration/settings:
mapUnderscoreToCamelCase; true | false 默认值False
是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。
type-aliases-package:
类型别名可为 Java 类型设置一个缩写名字。意在降低冗余的全限定类名书写
日志配置
logging.level.*
日志级别严重性映射。例如,`日志级别. org.springframework=DEBUG `
logging.pattern.dateformat yyyy-MM-dd HH:mm:ss.SSS
日志日期格式的附加模式。仅支持默认的回退设置。
/*
server:
port: 8082
spring:
datasource:
url: jdbc:mysql://localhost:3306/xxx?useSSL=false
username: xxx
password: xxx
driver-class-name: com.mysql.jdbc.Driver
mybatis:
type-aliases-package: cn.itcast.user.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS */
//mybatis-plus常用配置===============================================================
idType
类型:com.baomidou.mybatisplus.annotation.IdType
默认值:ASSIGN_ID
全局默认主键类型
tablePrefix
类型:String
默认值:null
表名前缀
logImpl
指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
/*mybatis-plus:
global-config:
db-config:
table-prefix: tb1_
id-type: auto
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl*/
//springboot常用日志配置=================================================================
logging.pattern.level
日志级别的附加器模式。仅支持默认的回退设置。
//druid常用配置============================================================================
/* spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/数据库名
username: xxx
password: xxx
filter:
wall:
config:
strict-syntax-check: false //是否开启语法检查
function-check: false */ //是否开启函数检查
/*
declarative:加注解
programmatic:写代码
什么是微服务呢?
就是将一个大的应用,拆分成多个小的模块,每个模块都有自己的功能和职责,每个模块可以
进行交互,这就是微服务。
微服务架构的风格:
就是将单一程序开发成一个微服务,
每个微服务运行在自己的进程中,并使用轻量级通信机制,通常是
HTTP RESTFUL API 。这些服务围绕业务能力来划分构建的,并通
过完全自动化部署机制来独立部署这些服务可以使用不同的编程语
言,以及不同数据存储技术,以保证最低限度的集中式管理
微服务有三大难题:
服务故障的传播性(熔断)、服务的划分和分布式事务
微服务的特点
1. 按业务(功能)划分为一个独立运行的程序,即服务单元。
2. 服务之间通过 HTTP 协议相互通信。 http 是一个万能的协议 (web 应用都支持的模式)
3. 自动化部署。
4. 可以用不同的编程语言。
5. 可以用不同的存储技术。
6. 服务集中化管理。
7. 微服务是一个分布式系统。
微服务的不足(正视它的不足)
微服务也不例外,微服务相对于单体应用来说具有很多的优势,当然也有它
的不足:
1. 微服务的复杂度
2. 分布式事务问题
3. 服务的划分(按照功能划分 还是按照组件来划分呢) 分工
4. 服务的部署(不用自动化部署 自动化部署
SpringCloud 常用组件表 (管家)
服务的注册和发现。(eureka,nacos,consul)
服务的负载均衡。(ribbon,dubbo)
服务的相互调用。(openFeign,dubbo)
服务的容错。(hystrix,sentinel)
服务网关。(gateway,zuul)
服务配置的统一管理。(config-server,nacos,apollo)
服务消息总线。(bus)
服务安全组件。(security,Oauth2.0)
服务监控。(admin) (jvm)
链路追踪。(sleuth+zipkin)
SpringCloud 就是微服务理念的一种具体落地实现方式,帮助微服务架构提供了必备的功能
目前开发中常用的落地实现有三种:
Dubbo+Zookeeper 半自动化的微服务实现架构 (别的管理没有)
SpringCloud Netflix 一站式微服务架构
SpringCloud Alibaba 新的一站式微服务架构
三大公司
Spring Netflix A
负载均衡,英文名称为 Load Balance(LB)http:// lb://(负载均衡协议),其含义
就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如 Web 服务器、
企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务
**/
/*eureka基础:========================================================================
springbootapplication类中,加上注解@EnableEurekaClient。
1 什么是 CAP 原则(面试)
问:为什么 zookeeper 不适合做注册中心?
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,
一致性(Consistency)
可用性(Availability)
分区容错性(Partition tolerance)(这个特性是不可避免的)
CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾
C : 数据的一致性 (A,B,C 里面的数据是一致的)
Zk 注重数据的一致性。
Eureka 不是很注重数据的一致性!
A: 服务的可用性(若 zk 集群里面的 master 挂了怎么办)Paxos(多数派)
在 zk 里面,若主机挂了,则 zk 集群整体不对外提供服务了,需要选一个新的出来(120s
左右)才能继续对外提供服务!
Eureka 注重服务的可用性,当 Eureka 集群只有一台活着,它就能对外提供服务
P:分区的容错性(在集群里面的机器,因为网络原因,机房的原因,可能导致数据不会里面
同步),它在分布式必须需要实现的特性!
Zookeeper 注重数据的一致性,CP zk(注册中心,配置文件中心,协调中心)
Eureka 注重服务的可用性 AP eureka
*/
//eureka服务发现
配置文件: spring.application.name=serviceName1
servlet:
@Autowired
private DiscoveryClient discoveryClient;
// 这就是服务发现 通过服务的应用名 找到服务的具体信息
List instances = discoveryClient.getInstances(serviceName1);
服务器主机name一般设为ip地址;
eureka-server本身即是服务端又是客户端,而是客户端就都要与注册它的服务端续约【发送心跳】,来表示客户端还活着。所以要注册defaultZone【不写,默认注册到8761/eureka】,和设置renewal-interval。
spring:
application:
name: eureka-server-xxx
如果设置集群,则不同server之间ip应该设为不同【也可以改配置文件进行ip映射】,端口号设置为不同的,应用name设置成相同的。两辆之间相互注册,所以要注册当前defaultZone为另外两个server的注册地址。
win+R 输入drivers,进入etc/hosts文件,进行配置映射peer1,peer2,peer3。在设置主机name时,设置为映射地址即可。 在设置defaultZone时,主机名也设为映射地址。
交互地址即注册【传数据】地址;缓解服务列表脏读问题要配置interval,事件越短脏读越少
,但性能消耗越大。客户端就要与注册它的服务端续约【发送心跳】,来表示客户端还活着。
spring:
application:
name: eureka-client-xxx
/* Ribbon 概述:==========================================================================
Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix
Ribbon 实现。通过 Spring Cloud 的封装,可以让我们轻松地将面向服务的 REST 模版请求
自动转换成客户端负载均衡的服务调用。轮询、hash、权重...主要功能是提供客户端负载均衡算法和服务调用。
最好先启动provider,在启动consumer
Ribbon 是客户端实现负载均衡的远程调用组件,用法简单
Ribbon 源码核心:
ILoadBalancer 接口:起到承上启下的作用
1. 承上:从 eureka 拉取服务列表
2. 启下:使用 IRule 算法实现客户端调用的负载均
使用:1、导依赖
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
x.x.x.RELEASE
2、consummer配置类中声明RestTemplate时加注解@LoadBalanced,让其被ribbon来托管,
然后再在servlet中直接使用此RestTemplate
配置类 @Bean
@LoadBalanced 使用该注解后,这个restTemplate就不能用原生的url了,只能在servlet中new一个新RestTemplate
public RestTemplate restTemplate(){
return new RestTemplate();
}
*/
//consumer 和 provider-xxx 依赖都是 eureka-client
provider-xxx:
配置文件: spring.application.name=providerName1
servlet: @xxxmapping("yyy")
consummer-xxx:
配置文件: spring.application.name=consummerName1
servlet: restTemplate.getForObject("http://" + providerName1 + "/yyy", String.class);
思考 路径不合法,ribbon是怎么将 http://providerName1/yyy 路径请求成功的
* 1.Ribbon拦截这个请求
* 2.截取主机名称
* 3.借助eureka来做服务发现,通过服务名称拿到服务端集合,list<>
* 4.通过负载均衡算法 拿到集合中的一个服务
* 5.reConstructURL 自动把url进行重构,把providerName1替换为这个服务对应的ip和port
* 6.发起请求
//Ribbon服务发现
【servlet】
@Autowired
private LoadBalancerClient loadBalancerClient;
ServiceInstance choose = loadBalancerClient.choose(serviceName1);
//Ribbon 负载均衡的实现和几种算法
在 ribbon 中有一个核心的负载均衡算法接口 IRule
1.RoundRobinRule--轮询 请求次数 % 机器数量
2.RandomRule--随机
3.权重
4. iphash
3.AvailabilityFilteringRule --会先过滤掉由于多次访问故障处于断路器跳闸状态的服
务,还有并发的连接数量超过阈值的服务,然后对于剩余的服务列表按照轮询的策略进行访问
4.WeightedResponseTimeRule--根据平均响应时间计算所有服务的权重,响应时间越快服
务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信
息足够会切换到自身规则
5.RetryRule-- 先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重
试,获取可用的服务
6.BestAvailableRule --会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后
选择一个并发量小的服务
7.ZoneAvoidanceRule -- 默认规则,复合判断 Server 所在区域的性能和 Server 的可用
行选择服务器。
Ribbon 默认使用哪一个负载均衡算法:
ZoneAvoidanceRule :区间内亲和轮询的算法!通过一个 key 来区
//如何修改默认的负载均衡算法
//其他配置
ribbon: #全局的设置
eager-load:
enabled: false # ribbon 一启动不会主动去拉取服务列表,当实际使用时才去拉取【懒加载】 是否立即加载
http:
client:
enabled: false # 在 ribbon 最后要发起 Http 的调用调用,我们认为是RestTemplate 完成的,
其实最后是 HttpURLConnection 来完成的,这里面设置为 true,可以把 HttpUrlConnection->HttpClient
okhttp:
enabled: false #HttpUrlConnection 来完成的,这里面设置为 true ,可以把
HttpUrlConnection->OkHttpClient(也是发 http 请求的,它在移动端的开发用的多)
//修改consumer的yml配置文件(指定某一个服务使用什么算法)
providerName1: //提供者的服务名称,那么访问该服务的时候就会按照自定义的负载均衡算法
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule //几种算法的全限定类名
#修改默认负载均衡算法,几种算法的全限定类名
# NFLoadBalancerClassName: #loadBalance 策略
# NFLoadBalancerPingClassName: #ping 机制策略
# NIWSServerListClassName: #服务列表策略
# NIWSServerListFilterClassName: #服务列表过滤策略
ZonePreferenceServerListFilter 默认是优先过滤非一个区的服务列表
/*
分布式服务调用思想:不同的服务【客户端或服务端】所执行的功能不同,当一个服务consumer需要另一个服务provider的某些功能时
则就要相互调用服务获取功能。provider需要提供其接口【servlet资源】。==============================================
Ribbon 做负载均衡,用 Eureka-Client来做服务发现,通过RestTemplate来完成服务调用,
服务之间相互调用终极方案是使用 OpenFeign
Feign 是声明性(注解)Web 服务客户端。Spring Cloud 添加了对 Spring MVC 注解的支持,并
支持使用 HttpMessageConverters,Spring Web 中默认使用的注解。Spring Cloud 集成
了 Ribbon 和 Eureka 以及 Spring Cloud LoadBalancer,以在使用 Feign 时提供负载平衡
的 http 客户端。要使用 Feign,要创建一个接口并对其进行注解.
Feign只是帮你封装了远程调用的功能 底层还是ribbon。
OpenFeign 主要基于接口和注解实现了远程调用
源码总结:面试
1. OpenFeign 用过吗?它是如何运作的?
在主启动类上加上@EnableFeignClients 注解后,启动会进行包扫描,把所有加了
@FeignClient(value=”xxx-service”)注解的接口进行创建代理对象通过代理对象,使用
ribbon 做了负载均衡和远程调用
2. 如何创建的代理对象?
当 项 目 在 启 动 时 , 先 扫 描 , 然 后 拿 到 标 记 了 @FeignClient 注 解 的 接 口 信 息 , 由
ReflectiveFeign 类的 newInstance 方法创建了代理对象 JDK 代理
3. OpenFeign 到底是用什么做的远程调用?
使用的是 HttpURLConnection (java.net)
4. OpenFeign 怎么和 ribbon 整合的?
在代理对象执行调用的时候
使用:
1.1、provider需要提供其接口【servlet资源 xxxMapping("providermethod1url")】
1.2、consumer要选择依赖:OpenFeign,EurekaDiscoveryClient,SpringWeb,Lombok
2、SpringBootApplication中加注解@EnableFeignClients、@EnableEurekaClient
3、建立feign文件夹下创建自定义接口 xxxI,并加注解@FeignClient,指定provider的服务名
4、consumer下的controller中,先注入xxxI创建的对象,然后调用对象的方法即可
*/
//1.1 provider
【controller】
@GetMapping("providermethod1url")
public String providerMethodName1(){
try {
// 服务的具体实现
TimeUnit.SECONDS.sleep(2);//模拟操作数据库等 耗时2s
} catch (InterruptedException e) {
e.printStackTrace();
}
return ...;
}
//3
*consumer
【feign】
@FeignClient(value = "providerName1")
public interface InterfaceName1 {
/*你需要调用哪个provider的服务,就写provider提供接口的方法签名
* 方法签名(就是包含一个方法的所有的属性,修饰符,返回值,注解...)*/
@GetMapping("providermethod1url")
String methodName1();
}
【controller】
@RestController
public class defController {
/**
* 接口是不能做事情的
* 如果想做事 必须要有对象
* 那么这个接口肯定是被创建出代理对象的
* 动态代理 jdk(java interface 接口 $Proxy ) cglib(subClass 子类)
* jdk动态代理 只要是代理对象调用的方法必须走invoke方法 java.lang.reflect.InvocationHandler.invoke
* (java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Autowired
public InterfaceName1 interfaceName1;
/**
* 浏览器(前端)-------> provider-service(/consumermethodurl1)-----RPC(feign)--->consumer-service(/providermethod1url)
* feign的默认等待时间时1s
* 超过1s就在直接报错超时
* @return
*/
@GetMapping("consumermethodurl1")
public String consumerMethodName1() {
// 这里需要发起远程调用
String s = interfaceName1.methodName1();
return s;
}
/* Feign 传参确保消费者和提供者的参数列表一致 包括返回值 方法签名要一致
1. 通过 URL 传参数,GET 请求,参数列表使用@PathVariable(“”)
2. 如果是 GET 请求,每个基本参数必须加@RequestParam(“”)
3. 如果是 POST 请求,而且是对象集合等参数,必须加@Requestbody 或者@RequestPar
如果不加注解,默认会把参数放在请求体中,但其请求体只能放一个类型的参数,所以最好加注解。*/
/**练习:
* url /doOrder/热干面/add/油条/aaa
* get传递一个参数
* get传递多个参数
* post传递一个对象
* post传递一个对象+一个基本参数
*/
* provider
【domain】
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder//构建者模式
public class Order {
private Integer id;
private String name;
private Double price;
private Date time;
}
【servlet】
@RestController
public class ParamController {
@GetMapping("testUrl/{name}/and/{age}")
public String testUrl(@PathVariable("name") String name, @PathVariable("age") Integer age) {
...
return "ok";
}
@GetMapping("oneParam")
public String oneParam(@RequestParam(required = false) String name) {
...
return "ok";
}
@GetMapping("twoParam")
public String twoParam(@RequestParam(required = false) String name, @RequestParam(required = false) Integer age) {
System.out.println(name);
System.out.println(age);
return "ok";
}
@PostMapping("oneObj")
public String oneObj(@RequestBody Order order) {
System.out.println(order);
return "ok";
}
@PostMapping("oneObjOneParam")
public String oneObjOneParam(@RequestBody Order order,@RequestParam("name") String name) {
System.out.println(name);
System.out.println(order);
return "ok";
}
//单独传递时间对象
@GetMapping("testTime")
public String testTime(@RequestParam Date date){
System.out.println(date);
return "ok";
}
*consumer
【application.yml】
server:
port: xxx
spring:
application:
name: user-service-xxx
eureka:
client:
service-url:
defaultZone: http://xxx/eureka
# feign只是帮你封装了远程调用的功能 底层还是ribbon 所以我们需要去修改ribbon的时间
ribbon:
ReadTimeout: 3000 # 给3s超时时间因为provider有一个耗时处理过程,所以要设计超时时间
ConnectTimeout: 3000 # 连接服务的超时时间
logging:
level:
com.zrx.feign.interfaceName1: debug # 我需要打印指定接口下面的日志
【applicationboot】
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.zrx.feign"}) // 开启feign的客户端功能 才可以帮助我们发起调用
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
/**
* 打印fein日志信息 级别
*/
@Bean
public Logger.Level level(){
return Logger.Level.FULL;
}
}
【feign】
@FeignClient(value = "provider-service-name")
public interface InterfaceName1 {
@GetMapping("testUrl/{name}/and/{age}")
public String testUrl(@PathVariable("name") String name, @PathVariable("age") Integer age);
@GetMapping("oneParam")
public String oneParam(@RequestParam(required = false) String name);
@GetMapping("twoParam")
public String twoParam(@RequestParam(required = false) String name, @RequestParam(required = false) Integer age);
@PostMapping("oneObj")
public String oneObj(@RequestBody Order order);
@PostMapping("oneObjOneParam")
public String oneObjOneParam(@RequestBody Order order, @RequestParam("name") String name);
@GetMapping("testTime")
public String testTime(@RequestParam Date date);
}
【servlet】
@RestController
public class UserController {
@Autowired
public UserOrderFeign userOrderFeign;
@GetMapping("testParam")
public String testParam(){
String cxs = userOrderFeign.testUrl("cxs", 18);
System.out.println(cxs);
String t = userOrderFeign.oneParam("老唐");
System.out.println(t);
String lg = userOrderFeign.twoParam("雷哥", 31);
System.out.println(lg);
/*传统构建对象
Order o=new Order();
o.setName(...);
o.setAge(...); */
//使用构建者模式
Order order = Order.builder()
.name("牛排")
.price(188D)
.time(new Date())
.id(1)
.build();
String s = userOrderFeign.oneObj(order);
System.out.println(s);
String param = userOrderFeign.oneObjOneParam(order, "稽哥");
System.out.println(param);
return "ok";
}
/**
*发送与接受时间差距 +- 14个小时
* 1.不建议单独传递时间参数
* 2.转成字符串 2022-03-20 10:25:55:213 因为字符串不会改变
* 3.jdk LocalDate 年月日 LocalDateTime 会丢失秒
* 4.改feign的源码
*
* @return
*/
@GetMapping("time")
public String time(){
Date date = new Date();
System.out.println(date);
String s = userOrderFeign.testTime(date);
LocalDate now = LocalDate.now();//年月日
LocalDateTime now1 = LocalDateTime.now();//年月日时分秒
return s;
}
}