使用步骤:SpringBoot
SpringBoot准备步骤
1:添加依赖操作:
// 继承SpringBoot父依赖,导入springCloud,通用Mapper启动器,mysql驱动,添加web启动器,tk.mybatis 减少数据库的配置
1:添加父工程依赖:
// springBoot 父坐标 统一依赖管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<!-- springCloud -->
<!-- 通用Mapper启动器 -->
<!-- mysql驱动 -->
2:添加子工程依赖:
<!-- 添加web启动器-->
<!-- mysql驱动 -->
<!-- tk.mybatis 减少数据库的配置-->
2:全局配置:
// 配置端口,数据库配置,mybatis别名
通过 application.properties 或 application.yml(application.yaml) 来覆盖默认属性值,如有重叠属性,默认以Properties优先,形成自定义配置,
端口
数据库配置
mybatis 别名
3:启动类:
// 添加注解 @SpringBootApplication , @MapperScan("com.itcast.user.mapper")映射器扫描 添加了 tk.mybatis 依赖才需要
@SpringBootApplication
@MapperScan("com.itcast.user.mapper") // <- 映射器扫描 添加了 tk.mybatis 依赖才需要
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4:编写controller:
// 加注解 @RestController , @RequestMapping("/user")
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User queryById(@PathVariable("id")Long id){
return userService.queryById(id);
}
}
5:编写Service: // 加注解 @Service
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User queryById(Long id){
return userMapper.selectByPrimaryKey(id);
}
}
6:编写Mapper:
// 继承 Mapper<User> <- Mapper 导入 tk.mybatis.mapper 的包
public interface UserMapper extends Mapper<User> { // <- Mapper 添加的是 tk.mybatis.mapper 的包
}
7:编写pojo:
// 加注解 @Table(name = "tb_user"), @Data,@Id,@KeySql(useGeneratedKeys = true)
@Table(name = "tb_user")
@Data
public class User {
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
private String userName; // 用户名
private String password; // 密码
private String name;// 姓名
private Integer age;// 年龄
private Integer sex;// 性别,1男性,2女性
private Date birthday;// 出生日期
private Date created;// 创建时间
private Date updated;// 更新时间
private String note;// 备注
}
使用步骤: 拦截器
定义一个拦截器
public class LoginInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.debug("preHandle method is now running!");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
logger.debug("postHandle method is now running!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
logger.debug("afterCompletion method is now running!");
}
}
注册拦截器
@Configuration
public class MvcConfig implements WebMvcConfigurer{
/**
* 通过@Bean注解,将我们定义的拦截器注册到Spring容器
* @return
*/
@Bean
public LoginInterceptor loginInterceptor(){
return new LoginInterceptor();
}
/**
* 重写接口中的addInterceptors方法,添加自定义拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 通过registry来注册拦截器,通过addPathPatterns来添加拦截路径
registry.addInterceptor(this.loginInterceptor()).addPathPatterns("/**");
}
}
静态资源的默认存放路径:
// 常用 classpath:/static/路径
// ResourceProperties 类中定义了默认静态资源路径:
- classpath:/static/ // 常用
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/public
创建返回结果实体类:
概念了解: VO , DTO , PO
- VO:(Value object 值对象 / View object 表现层对象):
主要是和表现层\页面打交道用
- DTO:(Data Transfer Object 数据传输(转换)对象):
主要用来封装和传输数据用。
- PO:(Persistent Object 持久化对象):
主要数据库表的映射
依赖:
```
<parent> // 父类
<properties> // 依赖管理
<dependencyManagement> // 统一依赖管理,不继承,子类pom文件加入后才继承
<dependencies> // 该父类下的所有子类都继承该些依赖
<build> // 插件
```
自动配置原理:
springBoot的自动配置: // 例如: SpringMVC 查看mvc 的自动配置类
@Configuration // 声明这个类是一个配置类
// 此处就是满足项目的类是是Type.SERVLET类型,也就是一个普通web工程
@ConditionalOnWebApplication(type = Type.SERVLET)
// Servlet只要引入了tomcat依赖自然会有,后两个需要引入SpringMVC才会有。
// 这里是判断是否引入了相关依赖,引入依赖后该条件成立,当前类的配置才会生效!
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
// 如果自己配置了一个WebMVCConfigurationSupport的类,那么这个默认配置就会失效!
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
总结:通过实现WebMvcConfigurer并添加@Configuration 注解来实现自定义部分SpringMvc配置。
SpringCloud主要框架:
- 服务发现 —— Netflix Eureka
- 服务调用 —— Netflix Feign
- 负载均衡 —— Netflix Ribbon
- 熔断器 —— Netflix Hystrix
- 服务网关 —— Netflix Zuul
- 分布式配置—— Spring Cloud Config
- 消息总线 —— Spring Cloud Bus
微服务的特点: 软件单一,松耦合,自包含,独立运行和部署的架构思想
微服务和分布式有什么区别?
微服务可以是分布式,分布式就是把业务进行拆分,把前台分开、service逻辑层以及common通用层分开,可认为是层级分离。
微服务侧重于功能的分离,
对于微服务来说,前台、后台、数据库等是完整的一套。
- 单一职责: 微服务中每一个服务都对应唯一的业务能力,做到单一职责
- 微: 微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。
- 面向服务: 面向服务是说每个服务都要对外暴露Rest风格服务接口API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供Rest的接口即可。
- 自治: 自治是说服务间互相独立,互不干扰
- 团队独立:每个服务都是一个独立的开发团队,人数不能过多。
- 技术独立:因为是面向服务,提供Rest接口,使用什么技术没有别人干涉
- 前后端分离:采用前后端分离开发,提供统一Rest接口,后端不用再为PC、移动段开发不同接口
- 数据库分离:每个服务都使用自己的数据源
- 部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护
微服务优点
1. 项目分解,拆分多个服务,解决了复杂性问题
3. 每个微服务独立的开发,部署
2. 各个服务只用提供API,让开发者可以自由选择开发技术
4. 让开发者只关注于一个业务功能,使得每个服务都很简单
5. 易于规模化开发,多个开发团队可以并行开发,每个团队负责一项服务
6. 改善故障隔离。一个服务宕机不会影响其他的服务
微服务缺点:
1. 测试工作更加困难
2. 部署复杂
3. 内存占用量更高
服务调用方式: RPC 和 HTTP
常见的远程调用方式有以下2种:
- RPC:Remote Produce Call 远程过程调用,类似的还有RMI(remote method invoke)。
自定义数据格式,基于原生TCP通信,速度快,效率高。
早期的webservice,现在热门的dubbo,都是RPC的典型代表.
- Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。
现在客户端浏览器与服务端通信基本都是采用Http协议,也可以用来进行远程服务调用。
缺点是消息封装臃肿,优势是对服务的提供和调用方没有任何技术限定,自由灵活,更符合微服务理念。
// 现在热门的Rest风格,就可以通过http协议来实现。
如果公司全部采用Java技术栈,那么使用Dubbo作为微服务架构是一个不错的选择。
如果公司的技术栈多样化,那么SpringCloud搭建微服务是不二之选。 因此使用Http方式来实现服务间调用
SpringCloud: 微服务是一种架构方式,最终肯定需要技术架构去实施。
微服务的实现方式很多,但是最火的莫过于Spring Cloud了。为什么?
- 后台硬:作为Spring家族的一员,有整个Spring全家桶靠山,背景十分强大。
- 技术强:Spring作为Java领域的前辈,可以说是功力深厚。有强力的技术团队支撑,一般人还真比不了
- 群众基础好:可以说大多数程序员的成长都伴随着Spring框架,试问:现在有几家公司开发不用Spring?SpringCloud与Spring的各个框架无缝整合,对大家来说一切都是熟悉的配方,熟悉的味道。
- 使用方便:相信大家都体会到了SpringBoot给我们开发带来的便利,而SpringCloud完全支持SpringBoot的开发,用很少的配置就能完成微服务框架的搭建
Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。
SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:
配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。
其主要涉及的组件包括
Netflix:
- Eureka: 注册中心 --> consul , zookeeper
- Feign: 服务调用
- Ribbon: 负载均衡
- Hystix: 熔断器
- Zuul: 服务网关
Eureka 注册中心: // 心跳机制 注册服务
Eureka详解:
- 心跳(续约):提供者定期通过http方式向Eureka刷新自己的状态
- Eureka-Server:就是服务注册中心(可以是一个集群),
提供服务注册和发现功能,对外暴露自己的地址。
- 提供者:启动后向Eureka注册自己信息(地址,服务名称等),
提供服务的应用,
- 消费者:服务调用方,会定期去Eureka拉取服务列表,
消费应用从注册中心获取服务列表,然后使用负载均衡算法选出一个服务进行调用。
Eureka注册中心 使用步骤:
1.引依赖:
// 服务端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
// 客户端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.配置文件:
spring.application.name // 属性来指定应用名称,将来会作为服务的id使用。
// 服务端
eureka:
client:
register-with-eureka: false # 不注册自己
fetch-registry: false #不拉取服务
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
defaultZone: http://127.0.0.1:10086/eureka
// 客户端
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
启动类的注解:
// 服务端 // 声明这个应用是一个EurekaServer
@EnableEurekaServer
// 客户端 // 开启Eureka客户端
@EnableDiscoveryClient // 推荐使用! 支持多种
@EnableEurekaClient // 不推荐! 只支持Enable
3.高可用的Eureka:
Eureka Server: 搭建 EurekaServer 的集群,形成高可用的Eureka中心
Eureka客户端:
客户端注册服务到集群: // 因为EurekaServer不止一个,因此注册服务的时候,service-url参数需要变化:
eureka:
client:
service-url: # EurekaServer地址,多个地址以','隔开
defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka
服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-erueka=true
默认 true。如果为true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息,
Eureka Server会把这些信息保存到一个双层Map结构中。
- 第一层Map的Key就是服务id,一般是配置中的spring.application.name属性
- 第二层Map的key是服务的实例id。一般host+ serviceId + port,例如:locahost:user-service:8081
- 值则是服务的实例对象,也就是说一个服务,可以同时启动多个不同实例,形成集群
默认注册时使用的是主机名,如果我们想用ip进行注册,可以添加配置:
eureka:
instance:
ip-address: 127.0.0.1 # ip地址
prefer-ip-address: true # 更倾向于使用ip,而不是host名
服务续约: // 需要最迟3分钟才能剔除挂掉的服务端,90秒检测并标记,60秒统一删除标记的服务端
在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求),告诉EurekaServer:“我还活着”。这个我们称为
4.服务的续约(renewal);
eureka:
instance:
lease-renewal-interval-in-seconds: 30 // 服务续约(renew)的间隔,默认为30秒
lease-expiration-duration-in-seconds: 90 // 服务失效时间,默认值90秒
5.获取服务列表:
当服务消费者启动是,会检测eureka.client.fetch-registry=true参数的值,
如果为true,则会从Eureka Server服务的列表只读备份,然后缓存在本地。
并且每隔30秒会重新获取并更新数据。我们可以通过下面的参数来修改:
eureka:
client:
registry-fetch-interval-seconds: 30
6.服务下线、失效剔除和自我保护
服务下线:当服务进行正常关闭操作时,
触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线了”。
失效剔除:当服务非正常关闭时,
默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除,这个操作被称为失效剔除。
可以通过eureka.server.eviction-interval-timer-in-ms参数对其进行修改,单位是毫秒。
自我保护:Eureka会统计服务实例最近15分钟心跳续约的比例 低于了85%
Eureka在这段时间内不会剔除任何服务实例,直到网络恢复正常。生产环境下这很有效,
保证了大多数服务依然可用,不过也有可能获取到失败的服务实例,
因此服务调用者必须做好服务的失败容错。
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
7.总结:
- Eureka 注册中心 负责管理、记录提供者的信息。
- 服务调用者把需求发送到Eureka,Eureka会调用相应的服务。实现了服务的自动注册、发现、状态监控。
- 服务的注册和发现都是可控制的,可以关闭也可以开启。默认都是开启
- 注册后需要心跳,心跳周期默认30秒一次,超过90秒没法认为宕机
- Eureka会有自我保护,当心跳失败比例超过阈值,那么开启自我保护,不再剔除服务。
- 服务拉取默认30秒拉取一次
- Eureka每个60秒会剔除标记为宕机的服务
- Eureka高可用就是多台Eureka互相注册在对方上,客服端通过相互复制完成数据同步
Feign 服务调用:
将rest请求进行隐藏,伪装成SpringMVC的Controller一样。Feign通过动态代理生成实现类。与mapper.xml类似
@FeignClient: 声明这是一个feign的客户端,类似于@mapper
在启动类@EnableFeignClients // 开启feign功能
Feign集成了robbon负载均衡,不需要自己定义RestTemplate了
快速入门
导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
开启Feign功能:
在启动类上,添加注解,开启Feign功能
@EnableFeignClients // 开启Feign功能
Feign的客户端:
- 首先这是一个接口,Feign会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像
- @FeignClient,声明这是一个Feign客户端,同时通过value属性指定服务名称
- 接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果
@FeignClient("user-service")
public interface UserClient {
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
改造原来的调用逻辑,使用UserClient访问:
@RestController
@RequestMapping("consumer")
@Slf4j
public class ConsumerController {
@Autowired
private UserClient userClient;
@GetMapping("{id}")
public User queryById(@PathVariable("id") Long id){
return userClient.queryById(id);
}
}
负载均衡:
RestTemplate的注册被删除了。Feign中已经自动集成了Ribbon负载均衡,因此我们不需要自己定义RestTemplate了
Fegin内置的ribbon默认设置了请求超时时长,默认是1000,我们可以通过手动配置来修改这个超时时长:
ribbon:
ReadTimeout: 2000 # 读取超时时长
ConnectTimeout: 1000 # 建立链接的超时时长
因为ribbon内部有重试机制,一旦超时,会自动重新发起请求。如果不希望重试,可以添加配置:
ribbon:
ConnectTimeout: 1000 # 连接超时时长
ReadTimeout: 2000 # 数据通信超时时长
MaxAutoRetries: 0 # 当前服务器的重试次数
MaxAutoRetriesNextServer: 0 # 重试多少次服务
OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试
Hystix 熔断器:
Hystix 熔断器: // Feign默认也有对Hystix的集成:
默认情况下是关闭的。需要通过下面的参数来开启:
feign:
hystrix:
enabled: true # 开启Feign的熔断功能 但是,Feign中的Fallback配置不像Ribbon中那样简单了。
(1)首先,我们要定义一个类,实现刚才编写的UserFeignClient,作为fallback的处理类
@Component
public class UserClientFallback implements UserClient {
@Override
public User queryById(Long id) {
User user = new User();
user.setId(id);
user.setName("用户查询出现异常!");
return user;
}
}
(2)然后在UserFeignClient中,指定刚才编写的实现类
@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class)
public interface UserClient {
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
(3)重启测试:
我们关闭user-service服务,然后在页面访问:
请求压缩:
Spring Cloud Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。
通过下面的参数即可开启请求与响应的压缩功能:
feign:
compression:
request:
enabled: true # 开启请求压缩
response:
enabled: true # 开启响应压缩
同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置:
feign:
compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
注:上面的数据类型、压缩大小下限均为默认值。
日志级别:
前面讲过,通过logging.level.xx=debug来设置日志级别。然而这个对Fegin客户端而言不会产生效果。
因为@FeignClient注解修改的客户端在被代理时,都会创建一个新的Fegin.Logger实例。
我们需要额外指定这个日志的级别才可以。
(1)设置cn.itcast包下的日志级别都为debug
logging:
level:
cn.itcast: debug
(2)编写配置类,定义日志级别
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
这里指定的Level级别是FULL,Feign支持4种级别:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
(3)在FeignClient中指定配置类:
@FeignClient(value = "user-service", fallback = UserClientFallback.class, configuration = FeignConfig.class)
public interface UserClient {
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
(4)重启项目,即可看到每次访问的日志:
Zuul网关: // 网关的核心功能是:过滤和路由
Zuul上实现限流: https://www.jianshu.com/p/d165e12df1da
不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。
一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现 鉴权、动态路由等等操作。
Zuul就是我们服务的统一入口。保证服务的安全性,需要进行权限控制,开放服务会破坏rest api的无状态特点。
操作步骤:
添加依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
加注解:
@EnableZuulProxy: // 开启Zuul的网关功能 添加在启动类
@SpringCouldApplication
配置文件:
server:
port: 10010 #服务端口
spring:
application:
name: api-gateway #指定服务名
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1zuul: #映射规则
zuul:
routes:
// 路由前缀
prefix: /api # 指定了路由的前缀,这样在发起请求时,路径就要以/api开头。
// 路径/api/user-service/user/1将会被代理到/user-service/user/1
user-service: /user-service/** # 指定服务名称: 这里是映射路径
// 因为有默认的路由规则,如果想要禁用某个路由规则,可以这样:
ignored-service: #忽略路由
- user-service #服务端的ID名
- consumer
...
过滤器: // 身份认证和安全 , 审查与监控 , 动态路由 , 压力测试 , 负载分配 , 静态响应处理 , 多区域弹性
1.ZuulFilter 是过滤器的顶级父类,其中定义的4个最重要的方法:
public abstract ZuulFilter implements IZuulFilter{
abstract public String filterType();
abstract public int filterOrder();
boolean shouldFilter();// 来自IZuulFilter
Object run() throws ZuulException;// IZuulFilter
}
- shouldFilter:返回一个Boolean值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
- run:过滤器的具体业务逻辑。
- filterType:返回字符串,代表过滤器的类型。包含以下4种:
- pre:请求在被路由之前执行
- route:在路由请求时调用
- post:在routing和errror过滤器之后调用
- error:处理请求时发生错误调用
- filterOrder:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
2.过滤器执行生命周期:
- 正常流程:
- 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。
- 异常流程:
- 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
- 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
- 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。
使用场景:
- 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
- 异常处理:一般会在error类型和post类型过滤器中结合来处理。
- 服务调用时长统计:pre和post结合使用。
3.自定义过滤器: // 基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。
4.负载均衡和熔断:
Zuul中默认 集成了 Ribbon负载均衡 和Hystix熔断机制
但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。
因此建议我们手动进行配置:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000
ribbon:
ConnectTimeout: 1000
ReadTimeout: 2000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
5.Zuul的高可用:
启动多个Zuul服务,自动注册到Eureka,形成集群。
内部访问:自动负载均衡
外部访问:使用其他的服务网关, 比如:Nginx
Ribbon 负载均衡 :
启动了多个server后,导致消费者不知道调用哪一个服务,这时使用Eureka所集成的Ribbon
负载均衡策略:轮询
如果要修改策略(随机),则要修改消费者中的yml文件,由SpringBoot提供,
User-service:
Ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalance.RandomRule
使用步骤:
引依赖:
不需要,在Eureka中包含了该依赖
启动类的注解:
// 客户端 的启动类 restTemplate方法上
@LoadBalanced
客户端的表现层代码:
// 修改调用方式,不再手动获取ip和端口,而是直接通过服务名称调用:
@GetMapping("{id}")
public User queryById(@PathVariable("id") Long id){
String url = "http://user-service/user/" + id; // 直接通过服务名称调用
User user = restTemplate.getForObject(url, User.class);
return user;
}
负载均衡策略
SpringBoot也帮我们提供了修改负载均衡规则的配置入口:
user-service: // 服务名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule // 值就是IRule的实现类
附加:
Ribbon默认是采用懒加载,即第一次访问时才会去创建负载均衡客户端。
往往会出现超时。如果需要采用饥饿加载,即项目启动即创建,可以这样配置
ribbon:
eager-load:
enabled: true
Hystrix 熔断器: // 一种保护机制 雪崩问题
Hystix解决雪崩问题的手段主要是服务降级,包括:
- 线程隔离
- 服务熔断
服务降级: 触发Hystix服务降级的情况:
- 线程池已满
- 请求超时
Hystrix为每个依赖服务调用分配一个小的线程池,
如果线程池已满调用将被立即拒绝,默认不采用排队.加速失败判定时间。
用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,
如果线程池已满,或者请求超时,则会进行降级处理
用户的请求故障时,不会被阻塞,至少可以看到一个执行结果(例如返回友好的提示信息).
操作步骤:
1.引入依赖: // 在所需要的服务端添加
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.开启熔断:
@EnableCircuitBreaker 在启动类上添加注解
@SpringCloudApplication 组合注解等同于
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker 在启动类上添加注解
3.编写降级逻辑: // 要使用HystixCommond来完成:
@GetMapping("{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallBack")
public String queryById(@PathVariable("id") Long id){
String url = "http://user-service/user/" + id;
String user = restTemplate.getForObject(url, String.class);
return user;
}
public String queryByIdFallBack(Long id){ // 用来声明一个降级逻辑的方法
log.error("查询用户信息失败,id:{}", id);
return "对不起,网络太拥挤了!";
}
4.默认的Fallback:
把Fallback配置加在类上,实现默认fallback:
@RestController
@RequestMapping("consumer")
@DefaultProperties(defaultFallback = "defaultFallBack") // 在类上指明统一的失败降级方法
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("{id}")
@HystrixCommand
public String queryById(@PathVariable("id") Long id){
String url = "http://user-service/user/" + id;
String user = restTemplate.getForObject(url, String.class);
return user;
}
public String defaultFallBack(){
return "默认提示:对不起,网络太拥挤了!";
}
}
5.超时设置:
请求在超过1秒后都会返回错误信息,这是因为Hystix的默认超时时长为1通过配置修改这个值:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000
这个配置会作用于全局所有方法。
6.服务熔断:
状态机有3个状态:
- Closed:关闭状态(断路器关闭),所有请求都正常访问。
- Open:打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全关闭。默认失败比例的阈值是50%,请求次数最少不低于20次。
- Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行5秒休眠计时。
7.熔断器的默认触发阈值是20次请求,不好触发。休眠时间时5秒,时间太短,不易观察,
为了测试方便,我们可以通过配置修改熔断策略:
circuitBreaker.requestVolumeThreshold=10 // 触发熔断的最小请求次数,默认20
circuitBreaker.sleepWindowInMilliseconds=10000 // 触发熔断的失败请求最小占比,默认50%
circuitBreaker.errorThresholdPercentage=50 // 休眠时长,默认是5000毫秒
Spring Cloud Config 分布式配置 :
服务配置文件的统一管理
配置服务器: 可横向扩展、集中式的配置服务器
配置客户端: 操作服务器的配置内容
spring-cloud-bus:消息总线 // 及时接收新的配置信息
Spring-cloud-stream:消息通信
spring-cloud-hystrix-dashboard:容错统计,形成图形化界面
spring-cloud-sleuth:链路追踪 结合Zipkin
微服务搭建:
创建父工程项目:
1. 导入依赖:
<packaging>pom</packaging>
<parent> // 继承父类 springBoot
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.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.SR2</spring-cloud.version>
<mapper.starter.version>2.1.4</mapper.starter.version>
<mysql.version>5.1.46</mysql.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.5</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>
<!-- 分页助手启动器 -->
<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>
<build> // 插件
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
创建子工程项目:
1. 创建服务的注册中心(EurekaServer):{
导入依赖:
<groupId>com.leyou.common</groupId>
<artifactId>ly-registry</artifactId>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.创建启动类:
注解:
@SpringBootApplication // 当前类是一个配置类
@EnableEurekaServer // Eureka 注册中心 服务端 -> 声明这个应用是一个EurekaServer
配置文件:
server:
port: 10086 // 端口
spring:
application:
name: ly-registry // 服务名
eureka:
client:
fetch-registry: false #是否检索服务 #不拉取服务
register-with-eureka: false #是否向服务注册中心注册自己 # 不注册自己
service-url:
defaultZone: http://127.0.0.1:10086/eureka
3. 创建服务网关(Zuul):{
导入依赖:
<groupId>com.leyou.common</groupId>
<artifactId>ly-gateway</artifactId>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
创建启动类:
注解:
@SpringBootApplication // 当前类是一个配置类
@EnableZuulProxy // 开启Zuul的网关功能 添加在启动类
配置文件:
server:
port: 10010
spring:
application:
name: api-gateway
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
zuul:
prefix: /api # 添加路由前缀
routes:
工程名-service: /工程名/** # 将商品微服务映射到/工程名/** 消除注释*/
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000 # 熔断超时时长:5000ms
ribbon:
ConnectTimeout: 1000 # ribbon链接超时时长
ReadTimeout: 3500 # ribbon读取超时时长
MaxAutoRetries: 0 # 当前服务重试次数
MaxAutoRetriesNextServer: 0 # 切换服务重试次数
-> 基础服务搭建完毕,为了便于开发,统一配置中心(ConfigServer)以后添加
创建商品微服务
1.创建子工程:
pom文件,打包方式设置为pom
2.在该工程下创建两个子工程:
- ly-子工程-pojo: 主要是相关实体类
- ly-子工程-service:所有业务逻辑及内部使用接口
添加依赖:{
<dependencies>
<dependency> // - web启动器
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency> // - Eureka客户端
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency> // - 通用mapper启动器
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
<dependency> // - mysql驱动
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency> // - 连接池,我们用默认的Hykira,引入jdbc启动器
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency> // - 分页助手启动器
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<dependency> // - ly-item-pojo中的实体类
<groupId>com.leyou.service</groupId>
<artifactId>ly-item-pojo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
配置文件:{
server:
port: 8081
spring:
application:
name: item-service
datasource:
url: jdbc:mysql://localhost:3306/heima54
username: root
password: 123456
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
hostname: localhost
}
}
3. 通用工具模块:
创建common工程:
引入依赖:
<groupId>com.leyou.common</groupId>
<artifactId>ly-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <!--日志启动器-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
<scope>compile</scope>
</dependency>
</dependencies>
引入工具类:
4. 日志配置:
SpringBoot默认采用的日志框架是:slf4j + logback的组合
在工具模块中 添加配置文件 logback-spring.xml :{
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>D:/heima/log/info.%d.log</fileNamePattern>
</rollingPolicy>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>20MB</MaxFileSize>
</triggeringPolicy>
</appender>
<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>D:/heima/log/error.%d.log</fileNamePattern>
</rollingPolicy>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>20MB</MaxFileSize>
</triggeringPolicy>
</appender>
<root level="info">
<appender-ref ref="consoleLog" />
<!--<appender-ref ref="fileErrorLog" />
<appender-ref ref="fileInfoLog" />-->
</root>
</configuration>
5. 通用异常处理:
场景预设: 假如我们做新增商品,需要接收下面的参数:price:价格 name:名称
然后对数据做简单校验:- 价格不能为空
新增时,自动形成ID,然后随商品对象一起返回
在ly-item-pojo中编写实体类:
package com.leyou.item.pojo;
import lombok.Data;
@Data
public class Item {
private Integer id;
private String name;
private Long price;
}
在ly-item-service中编写业务:
package com.leyou.item.service;
import com.leyou.item.pojo.Item;
import org.springframework.stereotype.Service;
import java.util.Random;
@Service
public class ItemService {
public Item saveItem(Item item){
// 这里临时使用随机生成id,然后直接返回,没有做数据库操作
int id = new Random().nextInt(100);
item.setId(id);
return item;
}
在ly-item-controller中编写:
package com.leyou.item.controller;
import com.leyou.item.pojo.Item;
import com.leyou.item.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ItemController<RestController> {
@Autowired
private ItemService itemService;
@PostMapping("item")
public ResponseEntity<Item> saveItem(Item item){
// 如果价格为空,则抛出异常,返回400状态码,请求参数有误
if(item.getPrice() == null){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
Item result = itemService.saveItem(item);
return ResponseEntity.status(HttpStatus.CREATED).body(result);
}
}
统一异常处理: // rest客户端:https://insomnia.rest/
返回错误信息时,没有任何有效提示,不友好.
修改controller的代码,把异常抛出:
@PostMapping("item")
public ResponseEntity<Item> saveItem(Item item){
if(item.getPrice() == null){
throw new RuntimeException("价格不能为空");
}
Item result = itemService.saveItem(item);
return ResponseEntity.ok(result);
}
6.使用SpringMVC提供的统一异常拦截器,因为是统一处理,我们放到ly-common项目中:{
package com.leyou.common.advice;
@ControllerAdvice // 默认情况下,会拦截所有加了@Controller的类
@Slf4j
public class BasicExceptionHandler {
// 声明要处理的异常类型,可以有多个;
@ExceptionHandler(RuntimeException.class){
// 被声明的方法可以看做是一个SpringMVC的Handler:
// - 参数是要处理的异常,类型必须要匹配
// - 返回结果可以是ModelAndView、ResponseEntity等,基本与handler类似
}
public ResponseEntity<String> handleException(RuntimeException e) {
// 这里等于从新定义了返回结果,我们可以随意指定想要的返回类型。此处使用了String
// 我们暂定返回状态码为400, 然后从异常中获取友好提示信息
return ResponseEntity.status(400).body(e.getMessage());
}
}
}
在ly-common中引入webmvc依赖:{
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
}
在ly-item-service中引入ly-common{
<dependency>
<groupId>com.leyou.common</groupId>
<artifactId>ly-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
}
自定义异常:{
仅凭抛出的异常,无法判断到底该返回怎样的状态码,可能是参数有误、数据库异常、没有权限,等等各种情况。
因此抛出异常时,就必须传递两个内容:
- 异常信息
- 异常状态码
// 定义一个枚举,用于封装这些信息:
在com.leyou.common.enums 中编写:{
@NoArgsConstructor
@AllArgsConstructor
public enum ExceptionEnum {
PRICE_CANNOT_BE_NULL(400, "价格不能为空!");
private int status;
private String msg;
public int status() { // status:响应状态码
return this.value;
}
public String msg() {
return this.msg;
}
}
}
// 自定义异常,来获取和传递枚举对象。
在com.leyou.common.exception 中编写:{
@Getter
public class LyException extends RuntimeException {
private int status;
public LyException(ExceptionEnum em) {
super(em.msg());
this.status = em.value();
}
public LyException(ExceptionEnum em, Throwable cause) {
super(em.msg(), cause);
this.status = em.value();
}
}
//status:响应状态码
}
修改controller代码:{
@PostMapping("item")
public ResponseEntity<Item> saveItem(Item item){
if(item.getPrice() == null){
throw new LyException(ExceptionEnum.PRICE_CANNOT_BE_NULL);
}
Item result = itemService.saveItem(item);
return ResponseEntity.ok(result);
}
}
在com.leyou.common.vo 中定义异常结果对象:{
@Getter
public class ExceptionResult {
private int status;
private String message;
private String timestamp;
public ExceptionResult(LyException e) {
this.status = e.getStatus();
this.message = e.getMessage();
this.timestamp = DateTime.now().toString("yyyy-MM-dd HH:mm:ss");
}
}
}
7.使用了日期工具类:JodaTime,我们要引入依赖在ly-common中:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
// 修改异常处理逻辑:
@ControllerAdvice
public class BasicExceptionHandler {
@ExceptionHandler(LyException.class)
public ResponseEntity<ExceptionResult> handleException(LyException e){
// 返回结果
return ResponseEntity.status(e.getStatus()).body(new ExceptionResult(e));
}
}
依赖查询:
父工程项目依赖:
<packaging>pom</packaging>
<parent> // 继承父类 springBoot
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<properties> // 统一依赖版本管理
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
......
</properties>
<dependencyManagement> // 统一依赖管理,子工程不添加,不继承
<dependencies>
<dependency> // <!-- springCloud -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency> // <!-- 通用Mapper启动器 -->
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>${mapper.starter.version}</version>
</dependency>
<dependency> // <!-- 分页助手启动器 -->
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pageHelper.starter.version}</version>
</dependency>
<dependency> // <!-- mysql驱动 -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency> // <!--FastDFS客户端-->
<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>
<build> // 插件
<plugins>
<plugin> // 将Spring Boot应用打包为可执行的jar或war文件,然后以通常的方式运行Spring Boot应用。
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
其他依赖:
<!-- tk.mybatis 减少数据库的配置 -->
<dependency> // - 连接池,我们用默认的Hykira,引入jdbc启动器
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependency> // webmvc 依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<scope>provided</scope>
</dependency>
工具类依赖:
<dependency> // <!--日志启动器-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>compile</scope>
SpringCloud主要框架依赖:
// web 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
// Eureka 注册中心 - 服务发现 —— Netflix Eureka
<dependency> // 服务端
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency> // 客户端
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
// Hystrix 熔断器
<dependency> // 在所需要的服务端添加
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
// Feign 服务调用
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
// Zuul 网关
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
配置查询:
server:
port: 0000 #服务端口
spring:
application:
name: api-gateway #指定服务名 // 属性来指定应用名称,将来会作为服务的id使用。
servlet:
multipart:
max-file-size: 5M # // 限制文件上传的大小
datasource: // 数据库配置
url: jdbc:mysql://localhost:3306/heima54
username: root
password: 123456
mybatis:
type-aliases-package: 别名扫描包位置
// 配置Eureka注册中心
// 服务端
eureka:
client:
register-with-eureka: false e# 不注册自己 #是否向服务注册中心注册自己 默认值true
fetch-registry: false #是否检索服务 #不拉取服务 默认值true
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
defaultZone: http://127.0.0.1:10086/eureka # EurekaServer地址,多个地址以','隔开
// Zuul 网关
instance:
prefer-ip-address: true # 使用ip服务注册,默认host名服务注册
ip-address: 127.0.0.1 #映射规则 // 默认注册时使用的是主机名,也可以ip注册
hostname: localhost
// 服务续约 服务下线、失效剔除 和 自我保护
lease-renewal-interval-in-seconds: 30 // 续约时间,默认30s
lease-expiration-duration-in-seconds: 90 // 服务时效时间,续约到期时间,默认90s
Server // 会自动剔除已经失效的服务,在开发中关闭自我保护
Enable-self-preservation: false 关闭自我保护,默认为打开
Eviction-interval-timer-in-ms: 1000 扫描失效的服务间隔,默认为 60*1000 ms
// Ribbon 负载均衡策略
user-service: // 服务名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule // 值就是IRule的实现类
ReadTimeout: 2000 # 读取超时时长
ConnectTimeout: 1000 # 建立链接的超时时长 默认是1000
MaxAutoRetries: 0 # 当前服务器的重试次数
MaxAutoRetriesNextServer: 0 # 重试多少次服务
OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试
eager-load:
enabled: true // Ribbon默认是懒加载,第一次访问时才会去创建负载均衡客户端。往往会出现超时。需要饥饿加载,即项目启动即创建,可以这样配置
// Ribbon 负载均衡策略
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000 // # 熔断超时时长:2000ms 默认请求在超过1秒后都会返回错误信息
// Netflix Hystrix 熔断器
feign: // 默认情况下是关闭的
hystrix:
enabled: true # 开启Feign的熔断功能
// Zuul 网关
zuul:
routes:
// 路由前缀
prefix: /api /# 指定路由的前缀,发请求时,路径就要以/api开头。 // 例如: 路径/api/user-service/user/1将会被代理到/user-service/user/1
工程名-service: /工程名-service/** # 指定服务名称: 这里是映射路径
// 因为有默认的路由规则,如果想要禁用某个路由规则,可以这样:
ignored-service: #忽略路由
- user-service #服务端的ID名
- consumer
...
注解查询:
未分类:
@Configuration: 声明一个类作为配置类,代替xml文件
@PropertySource: 指定外部属性文件路径
@Value: 属性注入
@Bean: 声明在方法上,将方法的返回值加入"Bean容器",代替<bean>标签
@ConfigurationProperties(prefix ="jdbc" ): // 读取配置文件
注解声明当前类为属性读取类:prefix="jdbc"读取属性文件中,前缀为jdbc的值
@EnableConfigurationProperties(使用的@ConfigurationProperties()的文件名.class):
声明要使用JdbcProperties(使用的@ConfigurationProperties()的文件名)这个类的对象
启动类:
类名上:
@SpringBootApplication:{ "组合注解 重要组成" // 声明当前类是一个配置类 // 核心注解
- @SpringBootConfiguration // 声明当前类是SpringBoot应用的配置类,项目中只能有一个
- @Configuration // Spring会自动扫描有该注解的类,并且读取其中的配置信息 // 标注了@Configuration之后,本身也是一个IoC容器。
- @EnableAutoConfiguration // 基于所添加的依赖,去“猜测”想要如何配置Spring // 可以将所有标注了@Configuration的javabean加入当前的IoC容器之中。
- @ComponentScan // -> 包扫描, 从声明这个注解的类所在的包开始,扫描包及子包 // 自动扫描并且加载符合条件的组件,并加入IoC容器之中。
}
@MapperScan("com.itcast.user.mapper") // <- 添加了 tk.mybatis 才需要这个依赖 mapper扫描包
// 配置Eureka注册中心
// 开启Eureka服务端
@EnableEurekaServer // 声明这个应用是一个EurekaServer 服务端
// 开启Eureka客户端
@EnableDiscoveryClient // 推荐使用! 支持多种
@EnableEurekaClient // 不推荐! 只支持Enable
// Feign 服务调用
@EnableFeignClients // 开启Feign功能
// Ribbon 负载均衡
@LoadBalanced // 客户端 的启动类
// Ribbon 负载均衡策略
@SpringCloudApplication:{ "组合注解"
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker 在启动类上添加注解
}
// Zuul 网关
@EnableZuulProxy: // 开启Zuul的网关功能 添加在启动类
controller类:
类名上:
@Slf4j
@CrossOrigin 支持跨域请求
@RestController:{ 组合注解 @RequestMapping + @ResponseBody
// @Controller 表示这是表现层
// @ResponseBody 表示把返回的对象转成json格式
}
@RequestMapping("/接收的参数名") 表示接收所有参数名和括号参数一致的消息
// Ribbon 负载均衡策略
@DefaultProperties(defaultFallback = "defaultFallBack") // 在类上指明统一的失败降级方法
// Feign 服务调用
@FeignClient("user-service") // Feign的客户端
属性上:
@Autowired // 自动注入
方法名上:
@GetMapping("/路径")
@PostMapping() 表示接收post请求
@PutMapping("/{参数}") 接收的参数
方法属性上:
@RequestBody 将请求信息自动导入到该对象中
@PathVariable 路径变量
// Ribbon 负载均衡策略
@HystrixCommand(fallbackMethod = "queryByIdFallBack")
service类:
类名上:
@Service 表示这是业务层
属性上:
@Autowired // 自动注入
方法名上:
@Transactional 加事务
持久层使用注解:
方法名上:
@Query 定义sql语句
@modifying 表示这是一个怎删改方法
实体类&工具类:
类名上:
@Entity 表示这是一个实体类
@Table(neme="表名") 表示该类与所填的表名进行绑定;可以不写,默认绑定与类名一致的表
@Data
属性上:
@Id 表示该属性对应的字段是这个表的主键
@GenerateValue 表示自动生成策略,不写就是生动生成策略
@Column(name="表字段") 表示如果属性名和字段名不一致那么使用该注解,在name后面填上字段名
// 需要在maven中引入 <groupId>org.projectlombok</groupId> 的依赖:
@Data :自动提供getter和setter、hashCode、equals、toString等方法以及构造函数
@Getter:自动提供getter方法
@Setter:自动提供setter方法
@Slf4j:自动在bean中提供log变量,其实用的是slf4j的日志功能。
@KeySql(useGeneratedKeys = true)
@ControllerAdvice // SpringMVC提供的统一异常拦截器 默认情况下,会拦截所有加了@Controller的类
异常类使用的注解:
使用在类名上:
@RestControllerAdvice 组合注解 @ControllerAdvice + @ResponseBody
// @ControllerAdvice 开启了对Controller增强的功能,使用当前类增强(AOP)
// @ResponseBody 表示把返回的对象转成json格式
使用在方法上:
@ExceptionHandler(指定的异常.class) 可以指定抓取具体异常,不填写就是抓取所有异常
可以使用在任何用@Controller注解修饰的类中,设置出现某种异常的时候执行,
如果修饰在@RestControllerAdvice或@RestControllerAdvice 修饰的类的方法上,则全局有效。
优先级:本类的优先级要高,本类 > 全局
跨域处理的注解:
使用在类名上:
@CrossOrigin 跨域处理
默认情况下,@CrossOrigin允许所有在@RequestMapping指定的所有HTTP方法的所有源,
即允许CrossOrigin允许所有跨域请求。
如果要指定允许跨域请求的源、最大缓存响应的周期、请求方法等,可以添加如下参数(参考):
...
@CrossOrigin(
origins = "http://anotherdomain.com", // 指定允许跨域请求的源
maxAge = 3600, // 最大缓存响应的周期
methods = {RequestMethod.GET, RequestMethod.POST} // 请求方法等
)
public class LabelController {
...
}
接口类查询:
持久层 -> 基础 Mapper<User> // <- Mapper 添加的是 tk.mybatis.mapper 的包
持久层的 Spring JPA 继承接口类:
JpaRepository<实体类类型,主键类型> 提供了基本的增删改查
JpaSpecificationExecutor<实体类类型> 用于做复杂的条件查询
toPredicate方法:
Root:查询哪个表
CriteriaQuery:查询哪些字段,排序是什么
CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式
Predicate(Expression):单独每一条查询条件的详细描述