微服务 Springcloud 架构设计

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 微服务现如今已成为炙手可热的新技术,似乎不谈点微服务技术,都显得不是那么主流了。微服务当中,Spring 家族尤为凸显的便是 Spring Boot,乃至 Spring Cloud。本篇主要介绍微服务 Springcloud 组件的多重设计。

1、 微服务简介

1.1 什么是微服务

所谓微服务,就是把一个比较大的单个应用程序或服务拆分为若干个独立的、粒度很小的服务或组件。

1.2 为什么使用微服务

微服务的拆解业务,这一策略,可扩展单个组件,而不需要整个的应用程序堆栈做修改,从而满足服务等级协议。微服务带来的好处是,它们更快且更容易更新。当开发者对一个传统的单体应用程序进行变更时,他们必须做详细、完整的 QA 测试,以确保变更不会影响其他特性或功能。但有了微服务,开发者可以更新应用程序的单个组件,而不会影响其他的部分。测试微服务应用程序仍然是必需的,但使得其更容易被识别和隔离,从而加快开发速度并支持 DevOps 和持续应用程序开发。

1.3 微服务的架构组成

这几年的快速发展,微服务已经变得越来越流行。其中,Spring Cloud 一直在更新,并被大部分公司所使用。代表性的有 Alibaba,2018 年 11 月左右,Spring Cloud 联合创始人 Spencer Gibb 在 Spring 官网的博客页面宣布:阿里巴巴开源 Spring Cloud Alibaba,并发布了首个预览版本。随后,Spring Cloud 官方 Twitter 也发布了此消息。Spring Cloud 的版本也很多:

Spring Cloud Spring Cloud Alibaba Spring Boot
Spring Cloud Hoxton 2.2.0.RELEASE 2.2.X.RELEASE
Spring Cloud Greenwich 2.1.1.RELEASE 2.1.X.RELEASE
Spring Cloud Finchley 2.0.1.RELEASE 2.0.X.RELEASE
Spring Cloud Edgware 1.5.1.RELEASE 1.5.X.RELEASE

以 Spring Boot1.x 为例,主要包括 Eureka、Zuul、Config、Ribbon、Hystrix 等。而在 Spring Boot2.x 中,网关采用了自己的 Gateway。当然在 Alibaba 版本中,其组件更是丰富:使用 Alibaba 的 Nacos 作为注册中心和配置中心。使用自带组件 Sentinel 作为限流、熔断神器。

2、 微服务之网关

2.1 常见的几种网关

目前,在 Spring Boot1.x 中,用到的比较多的网关就是 Zuul。Zuul 是 Netflix 公司开源的一个网关服务,而 Spring Boot2.x 中,采用了自家推出的 Spring Cloud Gateway。

2.2 API 网关的作用

API 网关的主要作用是反向路由、安全认证、负载均衡、限流熔断、日志监控。在 Zuul 中,我们可以通过注入 Bean 的方式来配置路由,也可以在直接通过配置文件来配置:

zuul.routes.api-d.sensitiveHeaders="*"
zuul.routes.api-d.path=/business/api/**
zuul.routes.api-d.serviceId=business-web

我们可以通过网关来做一些安全的认证:如统一鉴权。在 Zuul 中:

Zuul 的工作原理

  • 过滤器机制

zuul 的核心是一系列的 filters, 其作用可以类比 Servlet 框架的 Filter,或者 AOP。zuul 把 Request route 到用户处理逻辑的过程中,这些 filter 参与一些过滤处理,比如 Authentication,Load Shedding 等。几种标准的过滤器类型:

(1) PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

(2) ROUTING:这种过滤器用于构建发送给微服务的请求,并使用 Apache HttpClient 或 Netfilx Ribbon 请求微服务。

(3) POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

(4) ERROR:在其他阶段发生错误时执行该过滤器。

  • 过滤器的生命周期

filterOrder:通过 int 值来定义过滤器的执行顺序,越小优先级越高。

shouldFilter:返回一个 boolean 类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回 true,所以该过滤器总是生效。

run:过滤器的具体逻辑。需要注意,这里我们通过 ctx.setSendZuulResponse(false) 令 zuul 过滤该请求,不对其进行路由,然后通过 ctx.setResponseStatusCode(401) 设置了其返回的错误码。

代码示例:

@Component
publicclassAccessFilterextendsZuulFilter {
privatestatic Logger logger = LoggerFactory.getLogger(AccessFilter.class);

@Autowired
RedisCacheConfiguration redisCacheConfiguration;

@Autowired
EnvironmentConfig env;

privatestaticfinal String[] PASS_PATH_ARRAY = { "/login", "openProject" };

@Override
public String filterType() {
 return"pre";
}

@Override
publicintfilterOrder() {
 return0;
}

@Override
publicbooleanshouldFilter() {
 returntrue;
}

@Override
public Object run() {
 RequestContext ctx = RequestContext.getCurrentContext();
 HttpServletRequest request = ctx.getRequest();
 HttpServletResponse response = ctx.getResponse();
 response.setCharacterEncoding("UTF-8");
 response.setHeader("content-type", "text/html;charset=UTF-8");

 logger.info("{} request to {}", request.getMethod(), request.getRequestURL());
 for (String path : PASS_PATH_ARRAY) {
  if (StringUtils.contains(request.getRequestURL().toString(), path)) {
   logger.debug("request path: {} is pass", path);
   returnnull;
  }
 }

 String token = request.getHeader("token");
 if (StringUtils.isEmpty(token)) {
  logger.warn("access token is empty");
  ctx.setSendZuulResponse(false);
  ctx.setResponseStatusCode(404);
  ctx.setResponseBody(JSONObject.toJSONString(
    Response.error(200, -3, "header param error", null)));
  return ctx;
 }

 Jedis jedis = null;
 try {
  JedisPool jedisPool = redisCacheConfiguration.getJedisPool();
  jedis = jedisPool.getResource();
  logger.debug("zuul gateway service get redisResource success");
  String key = env.getPrefix() + token;
  String value = jedis.get(key);
  if (StringUtils.isBlank(value)) {
   ctx.setSendZuulResponse(false);
   ctx.setResponseStatusCode(401);
   ctx.setResponseBody(JSONObject.toJSONString(Response.error(200, -1, "login timeout",null)));
   return ctx;
  } else {
   logger.debug("access token ok");
   returnnull;
  }
 } catch (Exception e) {
  logger.error("get redisResource failed");
  logger.error(e.getMessage(), e);
  ctx.setSendZuulResponse(false);
  ctx.setResponseStatusCode(500);
  ctx.setResponseBody(JSONObject.toJSONString(
    Response.error(200, -8, "redis connect failed", null)));
  return ctx;
 } finally {
  if (jedis != null) {
   jedis.close();
  }
 }
}
}

3、 微服务之服务注册与发现

3.1 常见的几种注册中心

目前常见的几种注册中心有:Eureka、Consul、Nacos,但其实 Kubernetes 也可以实现服务的注册与发现功能,且听下面讲解。

Eureka 的高可用

在注册中心部署时,有可能出现节点问题,我们先看看 Eureka 集群如何实现高可用,首先配置基础的 Eureka 配置:

spring.application.name=eureka-server
server.port=1111

spring.profiles.active=dev

eureka.instance.hostname=localhost

eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

logging.path=/data/${spring.application.name}/logs

eureka.server.enable-self-preservation=false
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

eureka.server.eviction-interval-timer-in-ms=5000
eureka.server.responseCacheUpdateInvervalMs=60000

eureka.instance.lease-expiration-duration-in-seconds=10

eureka.instance.lease-renewal-interval-in-seconds=3

eureka.server.responseCacheAutoExpirationInSeconds=180

server.undertow.accesslog.enabled=false
server.undertow.accesslog.pattern=combined

配置好后,新建一个 application-peer1.properties 文件:

spring.application.name=eureka-server
server.port=1111
eureka.instance.hostname=peer1
eureka.client.serviceUrl.defaultZone=http://peer2:1112/eureka/

application-peer2.properties 文件:

spring.application.name=eureka-server
server.port=1112
eureka.instance.hostname=peer2
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/

这样通过域名 peer1、peer2 的形式来实现高可用,那么如何配置域名呢?有几种方式:

  • 通过 hosts 来配置域名,vi /etc/hosts:

10.12.3.2 peer1
10.12.3.5 peer2

  • 通过 kubernetes 部署服务时来配置域名:

hostAliases:
- ip: "10.12.3.2"
 hostnames:
 - "peer1"
- ip: "10.12.3.5"
 hostnames:
 - "peer2"

Nacos 实现服务注册、发现

Nacos 是 Alibaba 推出来的,目前最新版本是 v1.2.1。其功能可以实现服务的注册、发现,也可以作为配置管理来提供配置服务。可以手动去官网下载安装,Nacos 地址:https://github.com/alibaba/nacos/releases

执行,Linux/Unix/Mac:

sh startup.sh -m standalone

Windows:

cmd startup.cmd -m standalone

当我们引入 Nacos 相关配置时,即可使用它:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

注意:下面这个配置文件需要是 bootstrap,否则可能失败,至于为什么,大家可以自己试试。

spring:
 application:
   name: oauth-cas
 cloud:
   nacos:
     discovery:
       server-addr: 127.0.0.1:8848
     config:
       server-addr: 127.0.0.1:8848
       refreshable-dataids: actuator.properties,log.properties

配置完成后,完成 main:

package com.damon;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.damon"})
@EnableDiscoveryClient
public class CasApp {
 public static void main(String[] args) {
   SpringApplication.run(CasApp.class, args);
 }
}

完成以上,我们运行启动类,我们打开 Nacos 登录后,打开服务列表,即可看到:

在这里插入图片描述

Kubernetes 服务注册与发现

接下来,请允许我为大家引入 Kubernetes 的服务注册与发现功能,spring-cloud-kubernetes 的 DiscoveryClient 服务将 Kubernetes 中的 "Service" 资源与 Spring Cloud 中的服务对应起来了,有了这个 DiscoveryClient,我们在 Kubernetes 环境下就不需要 Eureka 等来做注册发现了,而是直接使用 Kubernetes 的服务机制。

在 pom.xml 中,有对 spring-cloud-kubernetes 框架的依赖配置:

<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-kubernetes-core</artifactId>
</dependency>

<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-kubernetes-discovery</artifactId>
</dependency>

为何 spring-cloud-kubernetes 可以完成服务注册发现呢?首先,创建一个 Spring Boot 项目的启动类,且引入服务发现注解 @EnableDiscoveryClient,同时需要开启服务发现:

spring:
 application:
   name: edge-admin
 cloud:
   kubernetes:
     discovery:
       all-namespaces: true

开启后,我们打开spring-cloud-kubernetes-discovery的源码,地址是:https://github.com/spring-cloud/spring-cloud-kubernetes/tree/master/spring-cloud-kubernetes-discovery,看到内容:

在这里插入图片描述

为什么要看这个文件呢?因为 spring 容器启动时,会寻找 classpath 下所有 spring.factories 文件(包括 jar 文件中的),spring.factories 中配置的所有类都会实例化,我们在开发 springboot 时常用到的***-starter.jar 就用到了这个技术,效果是一旦依赖了某个 starter.jar 很多功能就在 spring 初始化时候自动执行。

spring.factories 文件中有两个类:KubernetesDiscoveryClientAutoConfiguration 和 KubernetesDiscoveryClientConfigClientBootstrapConfiguration 都会被实例化。先看 KubernetesDiscoveryClientConfigClientBootstrapConfiguration,KubernetesAutoConfiguration 和 KubernetesDiscoveryClientAutoConfiguration 这两个类会被实例化:

* Copyright 2013-2019 the original author or authors.

package org.springframework.cloud.kubernetes.discovery;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.kubernetes.KubernetesAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@ConditionalOnProperty("spring.cloud.config.discovery.enabled")
@Import({ KubernetesAutoConfiguration.class,
 KubernetesDiscoveryClientAutoConfiguration.class })
publicclassKubernetesDiscoveryClientConfigClientBootstrapConfiguration
{

}

再看 KubernetesAutoConfiguration 的源码,会实例化一个重要的类 DefaultKubernetesClient,如下:

@Bean
@ConditionalOnMissingBean
public KubernetesClient kubernetesClient(Config config) {
returnnew DefaultKubernetesClient(config);
}

最后我们再看 KubernetesDiscoveryClientAutoConfiguration 源码,注意 kubernetesDiscoveryClient 方法,这里面是接口实现的重点,还要重点关注的地方是 KubernetesClient 参数的值,是上面提到的 DefaultKubernetesClient 对象:

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "spring.cloud.kubernetes.discovery.enabled", matchIfMissing = true)
public KubernetesDiscoveryClient kubernetesDiscoveryClient(KubernetesClient client,
  KubernetesDiscoveryProperties properties,
  KubernetesClientServicesFunction kubernetesClientServicesFunction,
  DefaultIsServicePortSecureResolver isServicePortSecureResolver)
{
 returnnew KubernetesDiscoveryClient(client, properties, kubernetesClientServicesFunction, isServicePortSecureResolver);
}

接下来,我们看 spring-cloud-kubernetes 中的 KubernetesDiscoveryClient.java,看方法:

public List<String> getServices(Predicate<Service> filter) {
 return this.kubernetesClientServicesFunction.apply(this.client).list().getItems()
   .stream().filter(filter).map(s -> s.getMetadata().getName())
   .collect(Collectors.toList());
}

在 apply(this.client).list(),可以看到数据源其实就是 this.client,并且 KubernetesClientServicesFunction 实例化时:

@Bean
public KubernetesClientServicesFunction servicesFunction(
  KubernetesDiscoveryProperties properties) {
 if (properties.getServiceLabels().isEmpty()) {
   return KubernetesClient::services;
 }

 return (client) -> client.services().withLabels(properties.getServiceLabels());
}

调用其 services 方法的返回结果,KubernetesDiscoveryClient.getServices 方法中的 this.client 是什么呢?在前面的分析时已经提到了,就是 DefaultKubernetesClient 类的实例,所以,此时要去去看 DefaultKubernetesClient.services 方法,发现 client 是  ServiceOperationsImpl:

@Override
 public MixedOperation<Service, ServiceList, DoneableService, ServiceResource<Service, DoneableService>> services() {
   return new ServiceOperationsImpl(httpClient, getConfiguration(), getNamespace());
 }

接着我们在实例 ServiceOperationsImpl 中看其 list 函数:

public L list() throws KubernetesClientException {
   try {
     HttpUrl.Builder requestUrlBuilder = HttpUrl.get(getNamespacedUrl()).newBuilder();

     String labelQueryParam = getLabelQueryParam();
     if (Utils.isNotNullOrEmpty(labelQueryParam)) {
       requestUrlBuilder.addQueryParameter("labelSelector", labelQueryParam);
     }

     String fieldQueryString = getFieldQueryParam();
     if (Utils.isNotNullOrEmpty(fieldQueryString)) {
       requestUrlBuilder.addQueryParameter("fieldSelector", fieldQueryString);
     }

     Request.Builder requestBuilder = new Request.Builder().get().url(requestUrlBuilder.build());
     L answer = handleResponse(requestBuilder, listType);
     updateApiVersion(answer);
     return answer;
   } catch (InterruptedException | ExecutionException | IOException e) {
     throw KubernetesClientException.launderThrowable(forOperationType("list"), e);
   }
 }

接着展开上面代码的 handleResponse 函数,可见里面是一次 http 请求,至于请求的地址,可以展开 getNamespacedUrl() 方法,里面调用的 getRootUrl 方法如下:

public URL getRootUrl() {
   try {
     if (apiGroup != null) {
       return new URL(URLUtils.join(config.getMasterUrl().toString(), "apis", apiGroup, apiVersion));
     }
     return new URL(URLUtils.join(config.getMasterUrl().toString(), "api", apiVersion));
   } catch (MalformedURLException e) {
     throw KubernetesClientException.launderThrowable(e);
   }
 }

我们看到逻辑中,貌似了解到其结果是这样的格式:

xxx/api/version 或 xxx/apis/xxx/version

看到这样的结果,感觉比较像访问 kubernetes 的 API Server 时用的 URL 标准格式,有关 API Server 服务的详情请参考官方文档,地址是: https://kubernetes.io/docs/reference/using-api/api-concepts/

弄清楚以上,我们发现了其实最终是向 kubernetes 的 API Server  发起 http 请求,获取 Service 资源的数据列表。因此,我们在最后还得在 k8s 底层新建 Service 资源来让其获取:

apiVersion: v1
kind: Service
metadata:
 name: admin-web-service
 namespace: default
spec:
 ports:
 - name: admin-web01
   port: 2001
   targetPort: admin-web01
 selector:
   app: admin-web

当然,在部署时,不管是以 Deployment 形式,还是以 DaemonSet 来部署,其最后还是 pod,如果要实现单个服务的多节点部署,可以用:

kubectl scale --replicas=2 deployment admin-web-deployment

总结:

spring-cloud-kubernetes 这个组件的服务发现目的就是获取 Kubernetes 中一个或者多个 Namespace 下的所有服务列表,且在过滤列表时候设置过滤的端口号 ,这样获取到服务列表后就能让依赖它们的 Spring Boot 或其它框架的应用完成服务发现工作,让服务能够通过 http://serviceName 这种方式进行访问。

4、 微服务之配置管理

4.1 常见的配置中心

目前常见的几种配置中心有:Spring Cloud Config、Apollo、Nacos,但其实 Kubernetes 组件 configMap 就可以实现服务的配置管理。并且,在 Spring Boot2.x 中,就已经引入使用了。

Nacos 配置中心

在上面注册中心中,我们讲到 Nacos,作为注册中心,其实也可以作为配置来管理服务的环境变量。

同样,引入其以依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

同样,注意:下面这个配置文件需要是 bootstrap,否则可能失败。

spring:
 application:
   name: oauth-cas
 cloud:
   nacos:
     discovery:
       server-addr: 127.0.0.1:8848
     config:
       server-addr: 127.0.0.1:8848
       refreshable-dataids: actuator.properties,log.properties

启动类在上面的注册中心已经讲过了,现在看其配置类:

package com.damon.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;


@Component
@RefreshScope
public class EnvConfig {

   @Value("${jdbc.driverClassName:}")
   private String jdbc_driverClassName;

   @Value("${jdbc.url:}")
   private String jdbc_url;

   @Value("${jdbc.username:}")
   private String jdbc_username;

   @Value("${jdbc.password:}")
   private String jdbc_password;
...

}

我们通过注解 @Component@RefreshScope,来实现其配置可被获取。注意 @Value("${jdbc.username:}")最后需要冒号的,否则启动后会报错的。

接下来可以配置属性值来,点击配置管理,查看配置:

在这里插入图片描述

如果首次打开没有配置,可以新建配置:

在这里插入图片描述

编辑配置:

在这里插入图片描述

新建完之后,可以编辑,也可以删除,这里就不操作了。

ConfigMap 作为配置管理

spring-cloud-kubernetes 在上面提供了服务发现的功能,其实它还很强大,也提供了服务的配置管理:

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-actuator-autoconfigure</artifactId>
 </dependency>

 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-kubernetes-config</artifactId>
 </dependency>

在初始化时,引入注解来自动注入:

package com.damon;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.damon.config.EnvConfig;

@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.damon"})
@EnableConfigurationProperties(EnvConfig.class)
@EnableDiscoveryClient
public class AdminApp {

   public static void main(String[] args) {
       SpringApplication.run(AdminApp.class, args);
   }

}

其中,EnvConfig 类来配置环境变量配置:

package com.damon.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "damon")
public class EnvConfig {

   private String message = "This is a dummy message";

   

   public String getMessage() {
       return this.message;
   }

   public void setMessage(String message) {
       this.message = message;
   }

}

这样,在部署时,我们新建 ConfigMap 类型的资源,同时,会配置其属性值:

kind: ConfigMap
apiVersion: v1
metadata:
 name: admin-web
data:
 application.yaml: |-
   damon:
     message: Say Hello to the World
   ---
   spring:
     profiles: dev
   damon:
     message: Say Hello to the Developers
   ---
   spring:
     profiles: test
   damon:
     message: Say Hello to the Test
   ---
   spring:
     profiles: prod
   damon:
     message: Say Hello to the Prod

并且结合配置,来实现动态更新:

spring:
 application:
   name: admin-web
 cloud:
   kubernetes:
     discovery:
       all-namespaces: true
     reload:
       enabled: true
       mode: polling
       period: 500
     config:
       sources:
       - name: ${spring.application.name}
         namespace: default

这里是实现自动 500ms 拉取配置,也可以通过事件触发的形式来动态获取最新配置:

spring:
 application:
   name: admin-web
 cloud:
   kubernetes:
     config:
       sources:
        - name: ${spring.application.name}
          namespace: default
     discovery:
       all-namespaces: true
     reload:
       enabled: true
       mode: event
       period: 500

5、 微服务模块划分

5.1 如何划分微服务

微服务架构设计中,服务拆分的问题很突出,第一种,按照纵向的业务拆分,第二种,横向的功能拆分。

以电商业务为例,首先按照业务领域的纵向拆分,分为用户微服务、商品微服务、交易微服务、订单微服务等等。

思考一下: 在纵向拆分仅仅按照业务领域进行拆分是否满足所有的业务场景?结果肯定是否定的。例如用户服务分为用户注册(写)和登录(读)等。写请求的重要性总是大于读请求的,在高并发下,读写比例 10:1,甚至更高的情况下,从而导致了大量的读请求往往会直接影响写请求。为了避免大量的读对写的请求干扰,需要对服务进行读写分离,即用户注册为一个微服务,登录为另一个微服务。此时按照 API 的细粒度继续进行纵向的业务拆分。

在横向上,按照所请求的功能进行拆分,即对一个请求的生命周期继续进行拆分。请求从用户端发出,首先接受到请求的是网关服务(这里不考虑 nginx 代理网关分发过程),网关服务对请求进行鉴权、参数合法性检查、路由转发等。接下来业务逻辑服务对请求进行业务逻辑的编排处理。对业务数据进行存储和查询就需要数据访问服务,数据访问服务提供了基本的 CRUD 原子操作,并负责海量数据的分库分表,以及屏蔽底层存储的差异性等功能。最后是数据持久化和缓存服务,比如可以采用 MQ、Kafka、Redis Cluster 等。

微服务架构通过业务的纵向拆分以及功能的横向拆分,服务演化成更小的颗粒度,各服务之间相互解耦,每个服务都可以快速迭代和持续交付(CI/CD),从而在公司层面能够达到降本增效的终极目标。但是服务粒度越细,服务之间的交互就会越来越多,更多的交互会使得服务之间的治理更复杂。服务之间的治理包括服务间的发现、通信、路由、负载均衡、重试机制、限流降级、熔断、链路跟踪等。

5.2 微服务划分的粒度

微服务划分粒度,其最核心的六个字可能就是:“高内聚、低耦合”。高内聚:就是说每个服务处于同一个网络或网域下,而且相对于外部,整个的是一个封闭的、安全的盒子,宛如一朵玫瑰花。盒子对外的接口是不变的,盒子内部各模块之间的接口也是不变的,但是各模块内部的内容可以更改。模块只对外暴露最小限度的接口,避免强依赖关系。增删一个模块,应该只会影响有依赖关系的相关模块,无关的不应该受影响。

那么低耦合,这就涉及到我们业务系统的设计了。所谓低耦合:就是要每个业务模块之间的关系降低,减少冗余、重复、交叉的复杂度,模块功能划分也尽可能单一。这样,才能达到低耦合的目的。

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
相关文章
|
1天前
|
监控 Java 开发者
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第13天】随着现代应用的复杂性日益增加,传统的单体应用架构已不足以满足快速迭代和可扩展性的需求。本文将探讨如何通过微服务架构来提升后端开发的效率和系统的可靠性,涵盖微服务设计原则、技术栈选择、部署策略以及维护实践。我们将分析微服务的优势与挑战,并提供一系列实施建议,帮助开发者在构建和维护分布式系统时做出明智决策。
|
1天前
|
存储 监控 API
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第13天】在现代软件开发中,随着业务需求的多样化和开发流程的复杂化,传统的单体应用架构逐渐显得笨重且难以适应快速变化。微服务架构作为一种新兴的分布式系统设计方式,以其灵活性、可扩展性和技术多样性受到广泛关注。本文旨在探讨微服务架构的核心概念、设计原则以及实施策略,为后端开发人员提供一种提升系统性能和开发效率的有效途径。
23 2
|
1天前
|
监控 持续交付 API
构建高效微服务架构:后端开发的新范式
【5月更文挑战第15天】 随着现代软件开发的演进,微服务架构已经成为企业解决复杂系统问题的首选方案。本文将深入剖析微服务的核心概念、设计原则及其在后端开发中的应用。我们将探讨如何通过容器化、服务网格和持续集成/持续部署(CI/CD)等技术手段提升系统的可伸缩性、弹性和维护性,同时确保高可用性和故障隔离。文章还将提供一系列实践案例,展示如何在实际项目中实施微服务架构,以及如何解决常见的挑战和问题。
25 1
|
1天前
|
敏捷开发 监控 API
构建高效可扩展的微服务架构
【5月更文挑战第15天】随着现代软件开发的复杂性日益增加,微服务架构已成为实现灵活、可维护和可扩展系统的关键方法。本文将探讨如何构建一个高效的微服务架构,包括关键的设计原则、技术选型以及常见的挑战和解决方案。通过实际案例分析,我们将展示如何利用容器化、服务网格和API网关等技术来优化服务的部署、管理和通信。
|
1天前
|
监控 测试技术 持续交付
构建高效可靠的微服务架构:后端开发的现代实践
【5月更文挑战第14天】 随着数字化转型的浪潮,企业对于灵活、可扩展且高效的后端系统的需求日益增长。本文旨在探讨如何通过微服务架构来实现这些需求,涵盖微服务设计原则、开发流程以及持续集成和部署(CI/CD)的最佳实践。文中还将讨论监控、日志管理与容错机制,以确保系统的可靠性和性能。
|
1天前
|
运维 负载均衡 监控
探索微服务架构下的服务治理策略
【5月更文挑战第14天】在当今软件开发的世界中,微服务架构因其灵活性、可扩展性和技术异构性而受到青睐。然而,随着系统向微服务模型迁移,服务治理成为确保系统整体稳定性和高效通信的关键。本文将探讨在微服务架构中实施有效服务治理的策略,包括服务发现、配置管理、负载均衡、熔断机制以及服务监控等关键要素。通过深入分析这些策略如何协同工作以维护系统的弹性和响应能力,我们旨在为开发和运维团队提供指导性的建议。
|
1天前
|
设计模式 API 持续交付
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第14天】在现代软件开发的快速迭代与多变需求中,传统的单体应用架构逐渐显露出其局限性。微服务架构作为一种新兴的分布式系统设计模式,以其灵活性、可扩展性及容错性受到广泛关注。本文将深入剖析微服务架构的核心概念,探讨其在后端开发中的应用,并提出一系列实施策略和最佳实践,以期帮助企业和技术团队更好地应对复杂多变的业务挑战。
|
1天前
|
负载均衡 持续交付 API
构建高效微服务架构的五大关键技术
【5月更文挑战第13天】在当前软件开发领域,微服务架构已经成为一种流行趋势。本文将探讨构建高效微服务架构的五大关键技术,包括容器化部署、服务发现与注册、API网关、负载均衡以及持续集成与持续部署。这些技术可以帮助开发团队更快速、更可靠地构建和部署微服务应用,提高系统的可扩展性和可维护性。
|
1天前
|
负载均衡 JavaScript Java
构建高效微服务架构:后端开发的新视角
【5月更文挑战第13天】在现代软件开发中,微服务架构已经成为一种流行趋势。它通过将应用程序拆分为一组小型、独立的服务来提高可扩展性、弹性和可维护性。本文将探讨如何构建一个高效的微服务架构,包括选择合适的技术栈、设计良好的服务接口、确保数据一致性以及实现有效的服务发现和负载均衡。
|
1天前
|
监控 持续交付 数据库
构建高效可靠的微服务架构:后端开发的新范式
【5月更文挑战第13天】 在当今软件开发的世界中,微服务架构已经成为了一种流行且有效的设计模式。它通过将大型复杂系统分解为一组独立的、可部署的服务来提高系统的可维护性、可扩展性和敏捷性。本文将探讨如何构建一个高效且可靠的微服务架构,包括关键的设计原则、技术选型以及可能面临的挑战。我们的目标是为后端开发者提供一套实用的指南,以便在构建现代化应用程序时做出明智的决策。