一、微服务的定义
微服务是一种软件的架构风格,将单个应用程序开发为“一套小型服务”的方法,每个服务“运行在自己的进程中”,并通过轻量级机制(HTTP、RPC)进行通信
单体应用架构 -> SOA(面向服务架构) -> 微服务
二、微服务的特点
- 传统单体架构的瓶颈
- 模块功能的耦合性高
- 更新需要全部代码重新编译
- 扩展困难(高可用、弹性扩展)
- 开发效率低
- 技术栈需要统一
- 微服务的优势
- 服务解耦
- 高可用、弹性扩容
- 高效开发
- 多技术栈共用
- 微服务的功能组件
- 注册中心
存放和调度服务,去中心化 (服务注册、服务发现)- 负载均衡
服务的弹性扩展- 网关
微服务的对外隔离- 熔断
预防服务雪崩、提高容错性- 内部服务调用
内部服务间的接口调用- 配置中心
统一的服务配置管理
常用的微服务框架:
SpringCloud 、 Dubbo
SpringCloud -Al
SpringCloid -T
三、SpringCloud
- 注册中心(Eureka、Nacos)
- 负载均衡 (Ribbon)
- 网关 (Zuul、gateway)
- 熔断、限流、降级 (Sentinel)
- 内部服务调用 (OpenFeign)
- 配置中心 (Config、Nacos)
- 链路追踪(Sleuth)
- 日志中心(ELK)
四、CAP原理
- Consistency 一致性
系统的数据信息(包括备份数据)在同一时刻都是一致的。 - Availability 可用性
服务在接收到客户端请求后,都能够给出响应 - Partition Tolerance 分区一致性
由于网络是不可靠的,所有节点之间很可能出现无法通讯的情况,在节点不能通信时,要保证系统可以继续正常服务
一个分布式系统中,CAP最多只能满足其中2个
CP AP CA
五、Eurek-Server(注册中心)
Spring Cloud Eureka是Spring Cloud Netflix微服务套件中实现服务治理的组件
满足AP
@EnableEurekaServer 开启注册中心,自动装配
配置文件
server:
port: XXXX
eureka:
instance:
hostname: localhost # 主机实例名
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
peer-node-read-timeout-ms: 3000
enable-self-preservation: false # 是否开启自我保护
eviction-interval-timer-in-ms: 60000
主要配置说明
1. server
- 是否允许开启自我保护模式,默认 true
当Eureka服务器在短时间内丢失过多客户端时,自我保护模式可使服务端不再删除失去连接的客户端
eureka.server.enable-self-preservation = false
- 节点读取的超时时间 毫秒
eureka.server.peer-node-read-timeout-ms
- 节点连接的超时时间 毫秒
eureka.server.peer-node-connect-timeout-ms
- 节点剔除的频率 毫秒
eureka.server.peer-node-connect-timeout-ms
- 总节点数
eureka.server.peer-node-total-connections
2. client
- Eureka服务器的地址,类型为HashMap,缺省的Key为 defaultZone;缺省的Value为 http://localhost:8761/eureka
eureka.client.service-url.defaultZone
如果服务注册中心为高可用集群时,多个注册中心地址以逗号分隔
- 是否可以自己注册自己 默认true
eureka.client.register-with-eureka = false
- 是否从注册中心拉取信息 默认true ,如果单节点不需要拉取
eureka.client.fetch-registry = false
3. instance
- 服务名
spring.application.name
- 主机实例名
eureka.instance.appname
六、Eureka-Client
配置文件
server:
port: XXXX
eureka:
instance:
prefer-ip-address: true
ip-address: XXX.XXXX.XX.XX
client:
service-url:
defaultZone: http://localhost:XXXX/eureka/
spring:
application:
name: XXX-client
七、OpenFeign (Rest Http 框架)
HTTP 形式的 Rest API 提供了非常简洁高效的 类RPC的 调用方式
- @EnableFeignClients 开启feign,自动装配
<!--引入feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- ribbon的超时时间
ribbon:
ReadTimeout: 30000
ConnectTimeout: 30000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
OkToRetryOnAllOperations: false
- 内部服务调用
@FeignClient(“XXX-client”) - 外部服务调用
@FeignClient(name=“XXX”,url=“XXX-client”)
@Autowired
private RestTemplate restTemplate;
- 其他
配置文件格式
properties、yml、xml
bootstrap.yml 程序引导时执行
application 优先级低于bootstarp
// pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.3</version>
</dependency>
八、网关(zuul、gateway)
Zuul-Server
- 重定向
- 负载均衡 ribon
- 请求过滤
@EnableZuulProxy 开启网关
@RestController
@RequestMapping("base")
@GetMapping("info")
localhost:6001/base/info
http://localhsot:6002/api/user/base/info
http://localhsot:6002/user-client/base/info
- 配置文件
zuul:
routes:
user-client: /api/user/**
order-client: /api/order/**
notify-client: /api/dispatch/**
ignored-patterns:
- /*-client/**
server:
port: 6060
eureka:
instance:
prefer-ip-address: true
client:
serviceUrl:
defaultZone: http://10.70.70.34:6010/eureka/
spring:
application:
name: zuul-server
servlet:
multipart:
enabled: true
max-file-size: 10MB
max-request-size: 10MB
http:
multipart:
enabled: true
max-file-size: 10MB
max-request-size: 10MB
ribbon:
ReadTimeout: 120000 #请求处理的超时时间
ConnectTimeout: 30000 #请求连接的超时时间
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000
过滤器
public class XXXFilter extends ZuulFilter{
/**
* 判断过滤器是否生效
* @return
*/
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String URI = request.getRequestURI();
if (URI.indexOf("/api") > -1 ){
return false;
}
return true;
}
/**
* 拦截后的业务逻辑
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
}
}
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
public static String createJWT(String id, String issuer, long ttlMillis,String KEY) throws Exception {
// 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 生成JWT的时间
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
// 创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
// 创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
Map<String, Object> claims = new HashMap<>();
claims.put("userid", "1267702530830254080");
claims.put("trancom", "11110001");
claims.put("name", "XXXXX");
// 生成签名的时候使用的秘钥secret,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。
// 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
SecretKey keys = generalKey(KEY);
// 下面就是在为payload添加各种标准声明和私有声明了
JwtBuilder builder = Jwts.builder() // 这里其实就是new一个JwtBuilder,设置jwt的body
.setClaims(claims) // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setId(id) // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
.setIssuedAt(now) // iat: jwt的签发时间
.setIssuer(issuer) // issuer:jwt签发人
//.setSubject(subject) // sub(Subject):代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
.signWith(signatureAlgorithm, keys); // 设置签名使用的签名算法和签名使用的秘钥
// 设置过期时间
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
return builder.compact();
}
/**
* 解密jwt
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt,String KEY) throws Exception {
SecretKey key = generalKey(KEY); //签名秘钥,和生成的签名的秘钥一模一样
Claims claims = Jwts.parser() //得到DefaultJwtParser
.setSigningKey(key) //设置签名的秘钥
.parseClaimsJws(jwt).getBody(); //设置需要解析的jwt
return claims;
}
/**
* 由字符串生成加密key
*
* @return
*/
private static SecretKey generalKey(String KEY){
byte[] encodedKey = Base64.decodeBase64(KEY);
SecretKeySpec keys = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return keys;
}
九、配置中心
spring-cloud-config
nacos
apollo
Config-Server
-
统一配置
-
简化运维
-
快速切换配置、参数
-
git作为后端配置存储(gitlab、github、码云、腾讯工蜂)
-
以版本控制系统作为文件系统的使用
-
本地文件系统作为存储库
-
使用jdbc作为后端存储库
-
redis作为后端存储库
-
组合多环境存储库
@EnableConfigServer
server:
port: 7301
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/xxx/test.git
searchPaths:
username: xxx
password: xxx
default-label: master
basedir: root/config_temp
force-pull: true
label: master
eureka:
instance:
prefer-ip-address: true
client:
serviceUrl:
defaultZone: http://localhost:6001/eureka/
客户端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.3</version>
</dependency>
spring:
cloud:
config:
uri: http://localhost:7301
label: master
profile: dev