Spring 简介
为什么要使用微服务
- 单体应用:
目前为止绝大部分的 web 应用软件采用单体应用,所有的应用的用户 UI、业务逻辑、数据库访问都打包在一个应用程序上。
缺点:
开发相互干扰,随着应用的不断升级沟通协调成本增加
应用上线由于某个功能升级导致需要整体的构建、整体测试、整体发布
- 微服务
把单体应用拆分成小的、松藕合分布式服务的形式
每个应用一定是独立构建、独立部署与测试,应用也是独立发布,应用于应用直接通常通过 restful API 接口的形式进行相互调用。
解决了单体应用带来的困扰。
Spring cloud 是什么
发展历史
2002,Rod Johonson 发表了 <<Expert One-on-One J2EE Design and Development>>,包含了 3 万行的代码在包 com.interface21 中
2003,Juerge Hoeller,Yann Caroff 联系 Rod,将书中代码开源,Yann 提出 Spring 这个词,冠于书中代码;并发布 0.9,使用 Apache 2.0 协议;Thomas Risberg 负责 Spring JDBC;Ben Alex 将 Acegi Security 贡献给 Rod 和 Juergen
2004,1.0 发布
2005,<<Professional Java Development with Spring Framework>> <<Pro Spring>> 出版;1.2.6 发布。AspectJ Leader Adrian Coyler 加入 Interface21 作为首席科学家;2006,Security 1.0、Spring webflow 1.0 发布;Spring 2.0 发布;2007,Spring Batch、WebService、Integration 发布;Spring 2.5 发布;2008,Spring Integration 1.0,Spring 2.5.6,Spring Batch 1.0;买了 g2One,一家提供 Groovy and Grails 的公司;2009,被 VMWare 发了 42 亿美金买下;Spring Python、STS 发布、3.0 发布(将包拆开,不提供超级包),买了 Cloud Foundry;2010,VMWare 买了 RabbitMQ 公司,获得 RabbitMQ 和 Redis 技术;2011,Spring 3.1、Spring AMQP、Spring Data JPA、Spring-data-common 1.0、Spring Data Redis、Spring Data Mongodb 发布;2012,Rod Johnson 离开 VMWare;Spring Android、Mobile 发布;2013,VMWare 和 EMC 合力组建了一家公司,Pivotal。Spring 4.0、Spring Boot 发布;2014,Spring 4.1.3、SpringBoot 1.0 发布;2015,Spring 4.2、4.3 发布;2016,Spring 4.3 GA
2017,Spirng 5.x
Spring 的出现让 EJB 等重量级的容器技术逐渐走向末路。
Spring 通过对 Bean 的生命周期的管理,可以快速方便的实现业务的逻辑处理。
Spring 可以方便的整合几乎所有的主流的开源项目如 JPA,缓存,消息组合等等,方便的进行开发。
Spring Cloud 实战
本实战目的:
全程演示如何创建一个基础的、可用的 Spring cloud 分布式应用系统
演示 Spring Cloud 各部分组件如何在应用之前协调、调用。
了解整个 Spring Cloud 的项目基本情况,有一个初步的认识。
本实战 JAVA 采用 JDK8
Spring Boot 版本采用最新 2.1.0 release.
Spring Cloud 版本采用 Greenwich.M1。
2.2.1. Spring Cloud 基础搭建
使用
阿里云的 spring cloud 架构图在线设计地址:https://www.freedgo.com 即可使用 Freedgo Desing 进行图形设计.
架构预览地址:
https://www.freedgo.com/draw_…://www.freedgo.com/templates/network/springcloud_aliyun.xml
Freedgo Design 是一 in 款在线绘制专业图形的网站。Freedgo Design 可以绘制各种类型的图形,针对业务逻辑的流程图,软件设计 ER 模板,工作流,各种云平台的系统部署架构图包括阿里云、AWS 云、腾讯云、Oracle、Asure 云、IBM 云平台等。
2.2.1.1. 建立基础代码
开发工具:Intellij idea
1、建立一个 mvn 工程项目
使用的 java jdk 采用 jdk8
2.2.1.2. 服务发现
建立子模块 discovery-service
我们可以看到 mvn 依赖导入了 netflix-eureka-server
Spring boot 会启动服务发现服务
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.2.1.3. 服务配置
建立子模块 config-service
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2.2.1.4. 服务路由
服务路由采用 Netflix Zuul
通过服务路由作为 eureka client,可以被发现服务监控
<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>
基础的组件创建完毕
2.2.1.5. 用户认证中心
2.2.1.6. 业务服务模块
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
2.2.2. 服务发现
传统服务发现方式通过网络 DNS 和负载均衡设备实现,需要网络和硬件设置支持,维护成本高,网络环境复杂
居于云的服务发现具有如下优点
高可用,热部署
负载均衡
健康检查,容错机制
抽象服务的逻辑名称
使用 Spring 与 Netflix Eureka 实现服务发现
实现服务发现功能需要配置服务发现端及需要注册服务配置客户端
1、发现服务端配置
服务注册需要 30 s 的时间才能显示在 Eureka 服务中,因为 Eureka 需要从服务接收 3 次连续心跳包 ping,每次心跳包 ping 间隔 10 s,然后才能使用这个服务。在部署和测试服务时,要牢记这一点。
application.yml
server:
port: 8761
# Eureka 服务器将要监听的端口
eureka:
client:
registerWithEureka: false #不要使用 Eureka 服务进行注册
fetchRegistry: false #不要在本地缓存注册表信息
使用一个新的注解 @EnableEurekaServer,就可以让我们的服务成为一个 Eureka 服务
2、服务发现客户端配置
以 config-service 为例
需要做 2 件事情
1、成为服务发现的客户端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、配置 application.yml(对应 config-server 来说我们只需要配置如下)
spring:
cloud:
config:
discovery:
enabled: true
启动运行查看
http://localhost:8761/eureka/apps/config-service。
应用作为服务发现的客户端设置
1、添加客户端依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、配置 application.yml
spring:
application:
name: business_service
eureka:
instance:
preferIpAddress: true
#注册服务的 IP,而不是服务器名称
client:
registerWithEureka: true #向 Eureka 注册服务
fetchRegistry: true
serviceUrl: #拉取注册表的本地副本
defaultZone: http://localhost:8761/eureka/ #Eureka 服务的位置
同样通过 mvn spring-boot:run 打开 http://localhost:8761 ,business_service 已经注册成功
使用服务发现查找服务
2.2.3. 用户认证中心
OAuth2 协议说明:
整体 OAuth 协议包括两方面:
1、访问授权:用户必须通过授权获取令牌
2、资源权限:通过授权的用户访问受保护的资源,根据定义访问权限来决定是否可以访问资源
配置说明:
启用 OAuth 授权服务
增加 @EnableAuthorizationServer 用于告诉 Spring Cloud,该服务将作为 OAuth2 服务
package com.yuaoq.train.business;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
@SpringBootApplication
@EnableResourceServer
@EnableAuthorizationServer
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);
}
}
OAuth 访问授权配置,配置注册的客户端应用程序
@Configuration
public class Auth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
// 覆盖 configure()方法。这定义了哪些客户端将注册到服务
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()
.withClient("yuaoq")
.secret("{noop}secret")
.authorizedGrantTypes(
"refresh_token",
"password",
"client_credentials")
.scopes("webclient","mobileclient");
}
// 该方法定义了 AuthenticationServerConfigurer 中使用的不同组件。这段代码告诉 Spring 使用 Spring 提供的默认验证管理器和用户详细信息服务
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
配置用户权限
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
@Bean
// AuthenticationManagerBean 被 Spring Security 用来处理验证
public AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();
}
// Security 使用 UserDetailsService 处理返回的用户信息,这些用户信息将由 Spring Security 返回
@Override
@Bean
public UserDetailsService userDetailsServiceBean() throws Exception {return super.userDetailsServiceBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {// configure()方法是定义用户、密码和角色的地方
auth.inMemoryAuthentication()
.withUser("admin")
.password("{noop}password")
.roles("ADMIN","USER")
.and()
.withUser("anyone")
.password("{noop}password")
.roles("USER");
}
}
获取用户信息(提供给其他服务获取用户信息使用)
@GetMapping(value = "/auth/user")
public Map<String, Object> user(OAuth2Authentication user) {Map<String, Object> userInfo = new HashMap<>();
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
return userInfo;
}
通过 postman 测试
自此提供了一个用户认证的微服务模块.
2.2.3.1. 业务模块微服务
1、对外提供 restful Api
@RestController: 由 spring web 提供的居于 restful 的接口标签
生成一个 restful api
@PostMapping("/list")
public ResponseEntity<List<String>> getBusiness() throws Exception {List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
return Optional.of(list)
.map(a -> new ResponseEntity<List<String>>(a, HttpStatus.OK))
.orElseThrow(() -> new Exception("error"));
}
使用 postman 调用接口
从 postman 返回的结果可以看到 401,未授权。
因为 business_service 服务引入了 spring-cloud-starter-security 那么默认是会对所有访问做安全控制。
2、服务的授权保护
现在 business/list 是未授权,那怎么配置一个受保护的 oauth2.0 资源,通过如下步骤
设置服务是一个受 oauth 保护的资源
定义应用的 OAuth 属性定义回调 URL
security:
oauth2:
resource:
user-info-uri: http://localhost:8282/auth/user
定义授权用户可以访问
@Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {// antMatchers()允许开发人员限制对受保护的 URL 和 HTTP DELETE 动词的调用
// hasRole()方法是一个允许访问的角色列表,该列表由逗号分隔
@Override
public void configure(HttpSecurity http) throws Exception {http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/v1/business/**")
.hasRole("ADMIN")
.anyRequest()
.authenticated();}
}
该段代码说明具有 ADMIN 角色的用户可以访问 /api/v1/business/ 下的所有的 POST 请求
验证如下:
至此通过 OAuth2.0 保护微服务的基本做法已经完成。
2.2.4. 服务路由网关
服务网关:服务客户端不再直接调用服务。取而代之的是,服务网关作为单个策略执行点(Policy Enforcement Point,PEP),所有调用都通过服务网关进行路由,然后被路由到最终目的地。
@EnabeZuulServer 使用此注解将创建一个 Zuul 服务器,它不会加载任何 Zuul 反向代理过滤器,也不会使用 Netflix Eureka 进行服务发现.
成为一个服务网关步骤:
1、添加 @EnableZuulProxy
2、在 application.yml 添加 route 规则
zuul:
sensitive-headers: set-cookies
routes:
business_service: /busi/**
通过 postman 测试如下: