从0开始搭建一个微服务后端系统-基础入门篇

前言

今天开始,我和大家一起,从0开始,基于Spring Cloud Alibaba,搭建一套基本的微服务架构的项目。

主要用到下面的知识内容

  • JDK8/IDEA/Maven
  • Spring/SpringBoot/Spring Cloud Alibaba
  • Dubbo/openfeign:服务间调用
    • 这里之所以引入了两种调用方式,是为了方便根据接口的实际情况,选择合适的调用方式
  • Seata:分布式事务
  • MySQL
  • Redis
  • RabbitMQ/RocketMQ
  • RBAC:这里,我不打算使用常见的 Spring Security/Spring Security OAuth2.0/Shiro等常见的认证授权技术,我打算自己手动撸一套简易的认证授权体系出来

之所以这么干,我还是考虑到一个定制化和灵活性的问题,在真正的大企业中,其认证授权体系是非常个性化的,如果用了某项技术,往往会制约这种个性化的实现。好吧,我这里主要想吐槽的就是 Spring Security太复杂了,做一个简单的功能,要理解的概念太多了。既然RBAC理论和OAuth2.0协议本身已经非常成熟了,我们根据自己的需要,手撸一个也不是什么大问题

理论:微服务与Spring Cloud

什么是微服务

首先,微服务本身,是一种软件设计,软件架构的思想,并不是具体的某一种技术。可千万别把微服务和Spring Cloud给划上等号。

  • 单体应用

我们先来说说传统的单体应用,也就是在一个项目中,把所有的代码都写在一个应用中。

这么做其实在后续功能不断迭代之后,在软件管理和设计上,是会带来种种问题的。

1、应用整体挂机的概率增强

我们写代码的时候要有一个底线思维,就是要把情况往糟糕的情况下去考虑,然后给出解决方案。

只有这样严格要求自己,我们的代码才会更加健壮。

但即使是这样,只要是人,就会犯错,并且,一个项目中的代码,迭代几轮之后,不知道经了多少人的手。

保不齐就有个小伙伴埋了个雷,一旦这个雷爆发,那么我们整个系统也就挂了。

2、技术受限制

当我们在已有的应用中添加功能代码的时候,是需要受当前应用的限制的。

比如说,我举个极端的例子,这个项目比较老了,使用的是Spring2.5,而现在我们做开发一般至少都用JDK8.

那么很不幸,Spring2.5和JDK8是不兼容的。于是我们就没法使用JDK1.8提供的各种实用的特性。

3、部署复杂

这个部署复杂,是针对代码量急剧增加之后来说的。

随着代码量越来越庞大,不管是启动还是测试,上线部署,都将变得越来越慢。

有时候明明改的是一个很小的功能,但是没办法,我们就是需要整个项目都部署一遍

  • 分布式

与单体应用相对的,就是分布式系统。

所谓分布式,就是说我们把一个项目,按照一定的划分原则,将各个功能的实现拆开来,部署的时候分开部署。

应用之间通过约定的方式调用。一般是http协议或者rpc协议(如 gRPC、thrift、Dubbo等)。

当我们按照功能拆开后进行部署和开发,带来的好处是很明显的,至少把上面讲的,单体应用的几个缺点,是都给解决掉了。

但是同时,也引入了新的问题

1、事务怎么办,在单体应用中,一个进程中操作一个数据库,回滚是非常方便的,直接使用数据库的特性就可以实现回滚

2、基础功能重复开发怎么办?即如何复用?各个模块中有些功能可能是公用的,一旦拆开后,怎么复用这些代码?

3、测试困难,原先只要启动一个应用就够了,现在如果一个功能的测试,依赖于多个模块,那么相关模块就都要运行起来

但是,后端开发进入现在这个阶段,我们对大数据量高并发的要求,相对于分布式系统的一点点缺点,其带来的好处是不言而喻的。

所以,现在基本上一家企业如果已经过了市场验证阶段,没理由继续使用单体应用,肯定都是要改造成分布式系统的。

  • 微服务

那么什么是微服务呢?

微服务肯定是分布式的。那么微服务和分布式系统又有什么区别呢?

以我自己不成熟的看法来说的话,所谓微服务,除了解决分布式系统领域的问题之外,其特点,重在一个微。

怎么样的一个服务,可以称之为微服务呢?这个才是难点。一个系统中,我们怎么拆分,不管是从业务的角度还是技术的角度,各个应用单元之间如何协作,这个才是微服务设计中最最复杂的部分,技术上的难点,其实互联网上的大厂,基本上都已经有解决方案了。

image-20200827150328805

微服务设计6大原则

  • 微服务和单体应用的选择

既然微服务有这么多优点,单体应用有那么多缺点,我们在实际项目中到底选择使用哪种开发模式呢?

这个其实和公司当前的实际情况是挂钩的,我们做开发的,一定要理清楚一点,技术,是为业务服务的,或者说是为了需求服务的。

千万不要为了技术而技术。如果一家公司,刚起步,人手不足、业务量也不大、技术实力也不够雄厚,这个时候贸贸然跟风上微服务,这不就是自己给自己找事儿做吗?

当公司业务上来了,团队规模也起来了,人员配备足够覆盖微服务本身的规模,能够驾驭了,这个时候,才能够切实感受到微服务对整个系统带来的好处。

Spring Cloud和微服务是什么关系

Spring Cloud,就是针对微服务的一套实现。

比较著名的所有 Spring Cloud Netflix和Spring Cloud Alibaba

小结

以上讲的都是一些概念性质的东西,具体怎么开发已经详细的理论,我会在后续实战部分,逐个进行讲解。

理论:微服务软件架构设计

我们先来对这一整个微服务体系,进行整体设计。

1598161368461

第一张图,是我在网上找的一个关于微服务架构的社交

第二张图,我重新精简了一下,基本上就划分为网关、认证中心、其他业务模块这几种类型。

实战:搭建项目骨架

  • chan
    • chan-dep:全局版本依赖管理
    • chan-common:通用工具类
    • chan-auth:认证授权模块
    • chan-user:用户管理模块
    • chan-log:日志模块
    • chan-gateway:网关
    • …:其他业务模块

我们创建了一个项目来进行依赖管理,其他大多数pom关系不大,

不过,对于Spring Cloud、Spring Cloud Alibaba、SpringBoot,这三者之间的版本,是有关联的。

Spring Cloud Alibaba的版本与Spring Boot一致

参见:https://start.spring.io/actuator/info

https://gitee.com/test-qqqq/spring-cloud-alibaba

Spring Cloud的版本与Spring Boot 一致

参考:https://spring.io/projects/spring-cloud/#overview

Spring Cloud Alibaba及其组件(Dubbo、Seata、Sentinel、Nacos),他们之间的版本也是有关联的。

我们在配置这几个组件的版本的时候,最好先去网上查一下,他们是怎么搭配的。

一般这种配置,除非是大版本的更新,否则一旦配置好后,就不要去修改了,避免引起不必要麻烦

理论:注册中心之Nacos

多个服务之间在相互调用的时候,是需要建立连接的。

那么他们怎么知道对方部署时的ip和port是多少呢?

  • 代码中写死?
    • 重启怎么办?
    • 扩容怎么办?

这个时候,就要用到注册中心。

  • 在微服务中,注册中心是个什么概念

注册中心主要是用来服务注册和发现的

实战:服务间简单调用

Nacos环境搭建

参考

git clone https://gitee.com/test-qqqq/nacos-docker.git

我这里就直接把nacos-docker clone到chan项目了,但是,我把nacos-docker加入了git忽略列表,不提交。

之所以将nacos-docker放到chan中,只是为了将所有与项目有关的物料,都放在此。

cd nacos-docker
# 启动单机版,用于开发测试足矣,生产上如果是部署在阿里云环境,可以购买服务,也不需要自己构建
docker-compose -f example/standalone-mysql-5.7.yaml up -d
# 停止
docker-compose -f example/standalone-mysql-5.7.yaml down

不过,这个standalone-mysql-5.7.yaml,里面的镜像速度超有点慢,我把他们都上传到自己的镜像仓库了,然后修改yaml文件如下

version: "2"
services:
  nacos:
    image: registry.cn-hangzhou.aliyuncs.com/sherry/nacos-server:latest
    container_name: nacos-standalone-mysql
    env_file:
      - ../env/nacos-standlone-mysql.env
    volumes:
      - ./standalone-logs/:/home/nacos/logs
      - ./init.d/custom.properties:/home/nacos/init.d/custom.properties
    ports:
      - "8848:8848"
      - "9555:9555"
    depends_on:
      - mysql
    restart: on-failure
  mysql:
    container_name: mysql
    image: registry.cn-hangzhou.aliyuncs.com/sherry/nacos-mysql:5.7
    env_file:
      - ../env/mysql.env
    volumes:
      - ./mysql:/var/lib/mysql
    ports:
      - "3306:3306"
  prometheus:
    container_name: prometheus
    image: registry.cn-hangzhou.aliyuncs.com/sherry/prometheus:latest
    volumes:
      - ./prometheus/prometheus-standalone.yaml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
    depends_on:
      - nacos
    restart: on-failure
  grafana:
    container_name: grafana
    image: registry.cn-hangzhou.aliyuncs.com/sherry/grafana:latest
    ports:
      - 3000:3000
    restart: on-failure

1598028159897

  • 验证

http://localhost:8848/nacos

默认账号密码均为nacos

image-20200822004419179

OpenFeign

  • 创建chan-auth和chan-user,通过OpenFeign,chan-auth调用chan-user

1598177656244

  • chan-user模块

1、编写一个正常的接口

@RequestMapping("/user")
@Slf4j
@RestController
public class UserController {
   

    @GetMapping(params = "userId")
    public R userInfo(@RequestParam String userId) {
   
        return R.ok("返回id为" + userId + "的用户信息");
    }

}

2、在api模块中暴露此接口

@FeignClient(name = "chan-user-svc")
public interface UserServiceApi {
   

    /**
     * 查询
     *
     * @param userId
     * @return
     */
    @GetMapping("/user")
    R user(@RequestParam(name = "userId") String userId);
}
  • chan-auth

1、引入user-api模块

        <dependency>
            <groupId>com.zhangln</groupId>
            <artifactId>chan-user-api</artifactId>
            <version>${project.parent.version}</version>
        </dependency>

2、auth模块调用user

1598177809150

3、auth模块中打开Feign支持

1598177840197

测试

  • 依次启动ChanAuthApplication和ChanUserApplication

image-20200823184318116

  • 调用接口

image-20200823184306702

实战:配置中心之Nacos

实战:网关

跨域问题

package com.zhangln.chan.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

@Configuration
public class CorsConfig {
   
    @Bean
    public CorsWebFilter corsFilter() {
   
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }

路由规则配置

spring:
  application:
    name: chan-gateway-svc
  main:
    allow-bean-definition-overriding: true
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: auth_route
          uri: lb://chan-auth-svc
          predicates:
            - Path=/auth/**
        - id: user_route
          uri: lb://chan-user-svc
          predicates:
            - Path=/user/**

全局日志

/**
 * 日志记录
 *
 * @author sherry
 * @description
 * @date Create in 2020/8/22
 * @modified By:
 */
@Component
@Slf4j
public class LogFilter implements GlobalFilter, Ordered {
   
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
   

        GatewayLogDto gatewayLogDto = new GatewayLogDto();
        gatewayLogDto.setId(UUID.randomUUID().toString().replace("-", ""));
        gatewayLogDto.setReqTime(LocalDateTime.now());

        log.info("{},日志过滤器--start", gatewayLogDto.getId());


        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpResponse serverHttpResponse = exchange.getResponse();

        gatewayLogDto.setUri(serverHttpRequest.getURI().getRawPath());
        HttpHeaders headers = serverHttpRequest.getHeaders();
        gatewayLogDto.setHeaderMap(headers);

        gatewayLogDto.setHost(headers.getHost().getHostName());


        MultiValueMap<String, String> queryParams = serverHttpRequest.getQueryParams();
        gatewayLogDto.setParamMap(queryParams);

        String method = serverHttpRequest.getMethodValue();
        gatewayLogDto.setMethod(method);
        String contentType = serverHttpRequest.getHeaders().getFirst("Content-Type");

        //向headers中放文件,记得build
        ServerHttpRequest host = null;
        try {
   

            URI newUri = null;
//            对URL参数进行URL编码
            if (!Objects.isNull(queryParams)) {
   
                StringBuilder query = new StringBuilder();
                Set<String> keySet = queryParams.keySet();
                for (Iterator<String> iterator = keySet.iterator(); iterator.hasNext(); ) {
   
                    String k = iterator.next();
                    String v = java.net.URLEncoder.encode
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
_size = enc_hiddens.shape[1] y_t = torch.tensor([self.vocab.tgt['<s>']] * batch_size,搭建一个微服务项目可以分为以下主要步骤: 1. 架构设计:确定微服务架构,包括 device=enc_hiddens.device).unsqueeze(0) # (1, batch_size) h_t, c_t = dec_init_state context = torch.zeros(batch_size, self.hidden_size*2, device=enc_hiddens.device) # (batch_size服务拆分、服务通信、服务治理等方面。 2. 技术选型:选择合适的技术栈, hidden_size*2) # initialize hypothesis hypo = [Hypothesis([self.vocab.tgt['<s>']], ,比如 Spring Cloud、Dubbo、ServiceComb 等。 3. 基础设施搭建搭建微服务基础设施,包括注册中心、配置中心、网关、链路追踪等。 4. 业务实现:0.0)] completed_hypo = [] for _ in range(max_length): # generate all possible next hypothesis 实现微服务业务逻辑,并进行集成测试、系统测试、性能测试等。 下面我将对每个 all_hypo = [] for h in hypo: if h.value[-1] == self.vocab.tgt['</s>']: 步骤进行详细的解释: 1. 架构设计: 在确定微服务架构时,需要考虑以下方面 completed_hypo.append(h) continue y_emb = self.model_embeddings.target(y_t) # (1, batch_size,: - 服务拆分:将业务功能拆分成不同的服务,每个服务都有独立的职责 embed_size) h_t, c_t, context, attention = self.step(y_emb.squeeze(0), h_t, c_t, enc和功能。 - 服务通信:服务之间需要通过某种方式进行通信,比如 RESTful API、RPC 等_hiddens, context) # h_t: (batch_size, hidden_size), c_t: (batch_size, hidden_size), context。 - 服务治理:需要对服务进行管理和监控,包括服务注册、服务发现、负载均衡: (batch_size, hidden_size*2), attention: (batch_size, src_sent_len) combined_output = self.combined_output、熔断降级等。 2. 技术选型: 在选择技术栈时,需要考虑以下方面: -_projection(torch.cat((h_t, context), dim=1)) # (batch_size, hidden_size) combined_output = torch.tanh 语言:选择适合你的业务场景的编程语言,比如 Java、Go、Python 等。 -(combined_output) target_vocab_dist = self.target_vocab_projection(combined_output) # (batch_size, tgt_vocab_size) 框架:选择适合你的业务场景的框架,比如 Spring Cloud、Dubbo、ServiceComb 等。 - topk_probs, topk_idx = target_vocab_dist.topk(beam_size, dim=1) # (batch_size, beam_size 中间件:选择适合你的业务场景的中间件,比如 Redis、MySQL、Kafka 等。 3) for i in range(beam_size): new_hypo = Hypothesis(h.value + [topk_idx[0][i. 基础设施搭建: 在搭建微服务基础设施时,需要考虑以下方面: - 注册].item()], h.score + topk_probs[0][i].item()) all_hypo.append(new_hypo) # sort hypothesis中心:用于服务注册和发现,比如 Eureka、Consul 等。 - 配置中心:用于配置 by descending score and select top k sorted_hypo = sorted(all_hypo, key=lambda h: h.score, reverse=True) hypo = sorted_hypo[:beam_size] # check if all hypothesis have completed if all([h.value[-1]管理,比如 Spring Cloud Config、Apollo 等。 - 网关:用于 API 网关和请求转发,比如 Zuul == self.vocab.tgt['</sGateway 等。 - 链路追踪:用于分布式链路追踪和性能监控,比如 Zipkin、SkyWalking 等。 4. 业务实现: 在实现微服务业务逻辑时,需要考虑以下方面: - 服务开发:编写业务逻辑代码,并进行单元测试和集成测试。 - 服务部署:将服务部署到云平台或服务器上,并进行系统测试和性能测试。 - 服务监控:对服务进行监控和管理,包括日志管理、指标监控、告警等。 以上是搭建一个微服务项目的主要步骤,需要注意的是,在实际项目中,还需要考虑很多其他方面的问题,比如安全、容灾、自动化运维等。如果你需要更多帮助,可以提出具体问题,我会尽力帮助你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值