SpringSecurity(/sɪˈkjʊrəti/ 安全)
在web开发中,安全第一位! 过滤器,拦截器 大量原生代码 太冗余了
- 漏洞,隐私泄露
- shiro 和 SpringSecurity 很相像 一个做认证 一个做授权
- shiro认证: 账号密码登陆
- SpringSecurity: vip1 vip2 vip8 权限不同
SpringSecurity简介:
Spring Security是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!
记住几个类:
- WebSecurityConfigurerAdapter(Web安全配置程序适配器): 自定义Security
- AuthenticationManagerBuilder(身份验证管理器): 自定义认证策略
- @EnableWebSecurity(启动网络安全): 开启WebSecurity模式
Spring Security的两个主要目标是"认证"和授权(访问控制)
“认证”(Authentication) /ɔːˌθentɪˈkeɪʃn/ a 凡提
“授权”(Authorization) /ˌɔːθərəˈzeɪʃn/
这个概念是通用的,而不是只在Spring Security中存在
//AOP
@EnableWebSecurity
public class SecurityController extends WebSecurityConfigurerAdapter {
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有人可以访问,功能页对应的人能访问
//请求授权的规则
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//没有权限,默认到登陆页,可自定义前端接收url,username,password
http.formLogin().loginPage("/login");
//开启注销功能,注销成功去首页 http.csrf().disable();测试中,关闭了跨站请求防护,不然注销404
http.logout().logoutSuccessUrl("/");
//开启记住我功能 cookie默认保存两周,自定义接收前端参数
http.rememberMe().rememberMeParameter("remember");
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常应该从数据库获取 auth.jdbcAuthentication(); passwordEncoder 加密密码 明文无效
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("zjt").password(new BCryptPasswordEncoder().encode("1")).roles("vip1")
.and()
.withUser("admin").password(new BCryptPasswordEncoder().encode("1")).roles("vip1","vip2")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("1")).roles("vip1","vip2","vip3");
}
}
Shiro
- Apache shiro 是一个java的安全(权限)框架
- Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaEE环境,也可以用在JavaSE环境
- Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等
- Shiro三大对象: Subject 用户; SecurityManager 管理所有用户 ; Realm 连接数据
- 工作流程简易:
- subject 封装信息: 框架提供的接口, 是与程序进行交互的对象,可以是人也可以是服务或者其他,通常就理解为用户。 接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权, 所有Subject 实例都必须绑定到一个SecurityManager上。我们与一个 Subject 交互,运行时shiro会自动转化为与 SecurityManager交互的特定 subject的交互。
- SecurityManager 安全管理器: SecurityManager即安全管理器, 对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等
- realm: Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息,相等于shiro和应用程序安全数据之间的 连接器. 他获取安全数据来判断subject是否能够登录,subject拥有什么权限。他有点类似DAO。常见的数据源有 ini文件数据源的IniRealm,properties文件数据源的PropertiesRealm Realms也是由SecurityManager控制
- 导入依赖
- 配置文件 int log4j
/**
* Simple Quickstart application showing how to use Shiro's API.
* 简单的快速启动应用程序显示如何使用Shiro的API。
* @since 0.9 RC2
*/
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 获取当前用户对象 Subject 安全工具.获得主题
Subject currentUser = SecurityUtils.getSubject();
// 通过当前用户拿到Session(shiro)
Session session = currentUser.getSession();
// 实现了存值取值
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("---> Retrieved the correct value! [" + value + "]");
}
// 判断当前的用户是否已经被认证, 即是否已经登录,调用 Subject 的 isAuthenticated()
if (!currentUser.isAuthenticated()) {
// 把用户名和密码封装为 UsernamePasswordToken 对象, 得到一个token(令牌)
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
// 设置记住我 rememberme
token.setRememberMe(true);
try {
// 执行登录.
currentUser.login(token);
}
// 若没有指定的账户
catch (UnknownAccountException uae) {
log.info("未知账户异常+令牌.委托人 " + token.getPrincipal());
return;
}
// 若账户存在,但密码不匹配
catch (IncorrectCredentialsException ice) {
log.info("错误的凭证异常+令牌.委托人 " + token.getPrincipal());
return;
}
// 用户被锁定的异常 LockedAccountException
catch (LockedAccountException lae) {
log.info("锁定账户异常+令牌.委托人 " + token.getPrincipal());
}
// 所有认证时异常的父类.
catch (AuthenticationException ae) {
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("----> User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role: 测试是否有某一个角色. 调用 Subject 的 hasRole 方法.
if (currentUser.hasRole("schwartz")) {
log.info("----> May the Schwartz be with you!");
} else {
log.info("----> Hello, mere mortal.");
return;
}
// 测试用户是否具备某一个权限. 调用 Subject 的 isPermitted() 方法。
if (currentUser.isPermitted("lightsaber:weild")) {
log.info("----> You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
// 测试用户是否具备某一个行为,更有力度的检测
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("你通过了更有力的检查");
} else {
log.info("Sorry, 你不被允许");
}
// 检测当前用户是否认证
System.out.println("---->" + currentUser.isAuthenticated());
// all done - log out! 全部完成-注销 调用 Subject 的 Logout() 方法.
currentUser.logout();
// 检测当前用户是否认证
System.out.println("---->" + currentUser.isAuthenticated());
// 退出系统
System.exit(0);
}
}
搭建springboot shiro
依赖:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.1</version>
</dependency>
controller:
@Controller
public class MyController {
// 从前端接收用户和密码
@RequestMapping("/login")
public String login(String username, String password, Model model) {
// 获取当前的用户
Subject subject = SecurityUtils.getSubject();
// 封装用户的登陆数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);// 执行登陆方法,如果没有异常就说明ok
return "index";
} catch (UnknownAccountException e) {
model.addAttribute("msg", "账号错误,未知用户异常");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误,错误凭证异常");
return "login";
}
}
// 未授权用户给予提示
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized() {
return "未经授权无法访问此页面";
}
}
shiroConfig:
@Configuration
public class ShiroConfig {
@Bean // 创建 Realm范围 对象,需要自定义类 : 步骤1
public UserRealm userRealm() {
return new UserRealm();
}
@Bean // DefaultWebSecurityManager 默认网站安全管理 : 步骤2
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 关联 Realm
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean // ShiroFilterFactoryBean shiro过滤器工厂bean : 步骤3
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
// 设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
// 添加shiro内置过滤器
/*
* anon : 无需认证就可以访问
* authc: 必须认证才能访问
* user : 必须拥有记住我功能,才能访问
* perms: 拥有某个资源的权限才能访问
* role : 拥有某个角色才可访问
*/
// 拦截,认证,验证
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/add", "authc");
filterMap.put("/update", "authc");
// 授权,正常情况下,未经授权会跳转到未授权页面
filterMap.put("/add", "perms[user:add]");
filterMap.put("/update", "perms[user:update]");
bean.setFilterChainDefinitionMap(filterMap);
// 设置登陆的请求
bean.setLoginUrl("/toLogin");
// 设置未授权路径
bean.setUnauthorizedUrl("/noauth");
return bean;
}
}
自定义realm:
// 自定义授权范围 AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
IUserService service;
// 认证,验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证");
// 调用 MyController 的 UsernamePasswordToken token = new UsernamePasswordToken(username, password);
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 假设输入的密码跟数据库的不一样
User user = service.queryUserByName(userToken.getUsername());
if (user == null) {
return null; // 自动抛出 UnknownAccountException 未知用户异常
}
// 密码认证 shiro做了加密 默认 SimpleCredentialsMatcher 简单密码加密
// 还可以MD5 e10adc3949ba59abbe56e057f20f883e MD5盐值加密
return new SimpleAuthenticationInfo(user, user.getPwd(), "");
}
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了授权");
// 固定代码,简单验证信息()
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 拿到当前登录的这个对象
Subject subject = SecurityUtils.getSubject();
// 获取 return new SimpleAuthenticationInfo(user,user.getPwd(),"");中的 Principal:user对象
User currentUser = (User) subject.getPrincipal();
// 设置当前用户权限:增加字符串许可
info.addStringPermission(currentUser.getPerms());
return info;
}
}
Swagger
- 号称世界上最流行的Api框架
- RestFul 风格Api 文档在线自动生成工具=>Api文档与API定义同步更新
- 直接运行,可以在线测试API接口(controller,resultMapping)
- 支持多种语言:(Java.php.Python….)
- 新建springboot web项目
- 导入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
- 编写一个Hello工程
- 配置Swagger==>Config
@Configuration
@EnableSwagger2 //开启Swagger2
public class SwaggerConfig {}
5.测试运行:http://localhost:8080/swagger-ui.html
配置Swagger
Swagger的bean实例 Docket(摘要)
//配置 Docket 的bean实例
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(true) //默认为true 如果为false 则Swagger在浏览器不能访问
.groupName("君不妄")
.select()
.apis(RequestHandlerSelectors.basePackage("com.zjt.controller"))
.build();
}
//配置swagger信息=apiInfo
private ApiInfo apiInfo(){
//作者信息 name,url,email
Contact CONTACT = new Contact("zjt", "www.123.com", "[email protected]");
return new ApiInfo(
"title:标题API",
"description:Api文档",
"version:2.0",
"termsOfServiceUrl:服务条款网址",
CONTACT,
"license:许可 Apache 2.0",
"licenseUrl: 许可链接http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
Swagger配置扫描接口
配置扫描的接口
//配置 Docket 的bean实例
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
/*配置要扫描接口的方式
basePackage():指定扫描的包
any():扫描全部
none():不扫描的
withClassAnnotation():扫描类上的注解,参数是一个注解的反射对象
withMethodAnnotation():扫描方法上的注解
*/
.apis(RequestHandlerSelectors.basePackage("com.zjt.controller"))
//paths 过滤条件
.paths(PathSelectors.ant("/zjt/**"))
.build();
}
配置是否启动Swagger
//配置 Docket 的bean实例
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(false) //默认为true 如果为false 则Swagger在浏览器不能访问
.select()
.apis(RequestHandlerSelectors.basePackage("com.zjt.controller"))
.build();
}
配置API文档的分组
.groupName("君不妄")
如何配置多个分组,多个Docket实例即可
@Bean
public Docket docket1(){
return new Docket(DocumentationType.SWAGGER_2).groupName("A");
}
@Bean
public Docket docket2(){
return new Docket(DocumentationType.SWAGGER_2).groupName("B");
}
常用注解说明
- @Api()用于类;
表示标识这个类是swagger的资源 - @ApiOperation()用于方法;
表示一个http请求的操作 - @ApiParam()用于方法,参数,字段说明;
表示对参数的添加元数据(说明或是否必填等) - @ApiModel()用于类
表示对类进行说明,用于参数用实体类接收 - @ApiModelProperty()用于方法,字段
表示对model属性的说明或者数据操作更改 - @ApiIgnore()用于类,方法,方法参数
表示这个方法或者类被忽略 - @ApiImplicitParam() 用于方法
表示单独的请求参数 - @ApiImplicitParams() 用于方法,包含多个 @ApiImplicitParam
总结
- 我们可以通过Swagger给一些比较难理解的属性或者接口增加注释信息
- 接口文档实时更新,更高效的协调开发
- 可以在线测试,详细明了
- 正式发布项目时要关闭Swagger
异步任务
为避免后端任务调度时间过久,导致前端页面无法展示内容,陷入加载或转圈或白屏,造成不好的用户体验,可以使用开启异步方法
在springboot入口类添加注解
@EnableAsync
在需要持久化超时的方法上
@Async
邮件发送
生成邮箱授权码:
配置application.properties
spring.mail.username=11567*****@qq.com
# 授权码
spring.mail.password=rvpijmeuycizfhjh
spring.mail.host=smtp.qq.com
# qq独有 开启加密验证
spring.mail.properties.mail.smtp.ssl.enable=true
代码实现:
//引入一个java邮件发送 实现类
@Autowired
JavaMailSenderImpl mailSender;
@Test
void test() {
//简单的邮件
SimpleMailMessage message = new SimpleMailMessage();
//正文
message.setSubject("君不妄"); //主题
message.setText("这是一封java测试邮件"); //内容
message.setTo("134****@qq.com"); //收件人
message.setFrom("1156****@qq.com"); //发件人
//邮件发送
mailSender.send(message);
}
@Test
void test2() throws MessagingException {
//一个复杂的邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
//组装 开启多文件 multipart:true
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
//正文
helper.setSubject("这是一个测试主题");
// 开启html功能 html:true
helper.setText("<p style='color:red'>这是一封java测试邮件</p>",true);
helper.setTo("1156****@qq.com");
helper.setFrom("11*****@qq.com");
//添加一个附件
helper.addAttachment("1.jpg",new File("C:\\Users\\Administrator\\Desktop\\鬼刀 (48).jpg"));
helper.addAttachment("2.jpg",new File("C:\\Users\\Administrator\\Desktop\\鬼刀 (49).jpg"));
//邮件发送
mailSender.send(mimeMessage);
}
定时器
@EnableScheduling 开启时程安排 : 在springboot入口类开启定时功能
@Scheduled 预定的: 什么时候执行
/*在一个特定时间执行这个方法
cron 表达式 秒 分 时 日 月 周几
"10 10 12 L * ?" 没有最后一天L 的12点,10分,10秒执行一次
"10 0/5 12,14 * * ?" 每天的12和14点,每隔5分钟,10秒执行一次
"0 0 0 27 10 ? *" 我的生日的零点祝福
cron:计时程序
*/
@Scheduled(cron = "0 0 0 27 10 ? *")
public void hello(){
System.out.println("生日快乐");
分布式Dubbo+Zookeeper+SpringBoot
什么是分布式系统:
在《分布式系统原理与范型》一书中有如下定义:”分布式系统是若干独立计算机的集合, 这些计算机对于用户来说就像单个相关系统"
分布式系统是由一-组通过网络进行通信、 为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。
分布式系统(distributed system)是建立在网络之上的软件系统。
首先需要明确的是,只有当单个节点的处理能力无法满足日益增长的计算、存储任务的时候,且硬件的提升(加内存、加磁盘、使用更好的CPU)高昂到得不偿失的时候,应用程序也不能进一步优化的时候,我们才需要考虑分布式系统。因为,分布式系统要解决的问题本身就是和单机系统一样的, 而由于分布式系统多节点、通过网络通信的拓扑结构,会引入很多单机系统没有的问题,为了解决这些问题又会引入更多的机制、协议,带来更多的问题。。。
Dubbo是什么:
Apache Dubbo |是一款高性能、轻量级的开源Java;RPC框架, 它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
Dubbo是一个分布式服务框架,RPC远程服务调用方案(RPC 解决通讯和序列化),以及SOA服务治理方案。简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是不需要用的,只有在分布式的时候,才有dubbo这样的分布式服务框架的需求,并且本质上是个服务调用的东东,说白了就是个远程服务调用的分布式框架
节点角色说明:
Provider: 暴露服务的服务提供方。
Consumer: 调用远程服务的服务消费方。
Registry: 服务注册与发现的注册中心。
Monitor: 统计服务的调用次调和调用时间的监控中心。
Container: 服务运行容器。
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Zookeeper
默认端口节点:2181
ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
dubbo-admin:
是一个监控管理后台~查看我们注册了那些服务,管理调优
步骤:
前提: zookeeper服务已开启!
-
提供者提供服务
-
导入依赖
-
配置注册中心的地址,以及服务发现名,和要扫描的包
# 服务应用name dubbo.application.name=provider-service # 注册中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181 # 要被注册服务的包路径 dubbo.scan.base-packages=com.zjt.service
- 在想要被注册的服务上面~增加一个注解 dubbo的@Service
import org.apache.dubbo.config.annotation.Service; import org.springframework.stereotype.Component; //zookeeper:服务注册与发现 @Service //可以被扫描到,项目一启动就自动注册到注册中心 @Component //使用了dubbo之后尽量不适用@Service注解,重名 public class FuwuServiceImpl implements FuwuService { @Override public String test() { return "服务 提供者 "; } }
-
-
消费者如何消费
- 导入依赖
- 配置注册中心的地址,配置自己的服务名
- 从远程注入服务 @Reference
Dubbo实现服务调用是通过RPC的方式,即客户端和服务端共用一个接口(将接口打成一个jar包,在客户端和服务端引入这个jar包),客户端面向接口写调用,服务端面向接口写实现,中间的网络通信交给框架去实现
聊聊现在和未来
三层架构 + MVC
架构 —> 解耦
开发框架Spring IOC AOP
IOC :控制反转
租房子以前需要自己一个一个跑,现在找中介,他那有房源,让中介帮我们完成租房子,原来我们都是自己一步步操作,现在交给容器了!我们需要什么就去拿就可以了
AOP :切面(本质,动态代理)
为了解决什么?不影响业务本来的情况下,实现动态增加功能,大量应用在日志,事务…等等方面
Spring是一个轻量级的J ava开源框架,容器
目的:解决企业开发的复杂性问题
Spring是春天,觉得他是春天,也十分复杂,配置文件!
SpringBoot
SpringBoot并不是新东西,就是Spring的升级版!
新一代JavaEE的开发标准,开箱即用! ->拿过来就可以用! 它自动帮我们配置了非常多的东西,我们拿来即用!
特性:约定大于配置!
随着公司体系越来越大,用户越来越多!
微服务架构—>新架构
模块化,功能化! 用户,支付,签到,娱乐,… ;
人多余多:一台服务器解决不了;在增加服务器 ! 横向
假设A服务器占用98%资源,B服务器只占用了10% 。一 负载均衡;
将原来的整体项目,分成模块化,用户就是一个单独的项目,签到也是一个单独的项目,项目和项目之前需要通信,如何通信?
用户非常多,而签到十分少! 给用户多一点服务器,给签到少一点服务器!
微服务架构问题?
分布式架构会遇到的四个核心问题?
- 这么多服务,客户端该如何去访问?
- 这么多服务,服务之间如何进行通信?
- 这么多服务,如何治理呢?
- 服务挂了,怎么办?
解决方案:
SpringCloud,是一套生态,就是来解决以上分布式架构的4个问题
想使用SpringCloud,必须要掌握SpringBoot,因为SpringCloud是基于SpringBoot;
-
Spring Cloud NetFlix ,出来了一套解决方案! 一站式解决方案。我们都可以直接去这里拿?
(1). Api网关,zuu1组件
(2). Feign --> HttpClient —> HTTP的通信方式,同步并阻塞
(3). 服务注册与发现,Eureka
(4). 熔断机制,Hystrix
2018年年底,NetFlix宣布无限期停止维护。生态不再维护,就会脱节。 -
Apache Dubbo zookeeper,第二套解决系统
(1). API :没有! 要么找第三方组件,要么自己实现
(2). Dubbo是一个高性能的基于Java实现的RPC通信框架! 2.6.x
(3). 服务注册与发现,zookeeper: 动物园管理者(Hadoop ,Hive)
(4). 没有: 借助了Hystrix
不完善,Dubbo 他们3.0融合了阿里的长处 推出后可能很强 -
SpringCloud Alibaba一站式解决方案 !还没成熟
目前,又提出了一种方案:
服务网格:下一代微服务标准,Service Mesh
代表解决方案: istio (你们未来可能需要掌握! )
- 万变不离其宗,一通百通!
- API网关,服务路由
- HTTP,RPC框架,异步调用
- 服务注册与发现,高可用
- 熔断机制,服务降级
为什么要解决这个问题? 本质:网络是不可靠的!