微服务架构 + 微信小程序

1.微服务架构

  • 如果国家比较小,可以直接把国王的命令传到每个百姓厕所前边。
  • 国家比较大,要一层层往下分

在这里插入图片描述


如果程序不是特别的大,可以直接使用一个项目来写。
如果程序稍微大点,可以借助 redis 实现分布式缓存。
如果再大的话,就需要用服架构了。

如果把这个思想一个用于电商系统,则图示如下:

在这里插入图片描述

在这个架构里面:下边的

  • 服务就是说 webApi ,
  • 商品负载就是 consul(领事): 他就负责将下边的功能登记在册,如果皇上需要,他就查查有没有,然后给皇上说声,然后让下边的士兵过去。
  • 网管就是 Ocelot (豹猫)

2. consul 的配置

consul是一个软件

1.去官网下载 consul 网址 https://www.consul.io/
2.在 consul 文件夹下cmd 输入: consul.exe agent -dev
3.其默认的端口号是:8500
在这里插入图片描述

直接在浏览器输入: http://localhost:8500,就可以调出 Services-Consul 窗口(这是下边第三部的东西配置完以后才有的界面)
在这里插入图片描述

在这里插入图片描述

配置 swagger 的使用,比较简单,就不写了

注意配置 swagger 的时候的一个坑,使用的时候,每个action方法上都要httpget方法,不然的话,会报下边的错误。


3.在 .net core 中手写 Consul 中间件

1.建立一个 ConsulManager 类,作为静态扩展文件,所有的类,包括方法,都必须要是静态的。

在包中引入Consul 中间件.

具体代码如下:

 public  static class ConsulManager
    {
        //扩展方法必须是静态的。
        public static void UseConsul(this IApplicationBuilder app, IConfiguration configuration, IConsulClient consul) 
        {
            Regserver(configuration,consul);
        }

        private static void Regserver(IConfiguration configuration, IConsulClient consul)
        {
            //微服务架构 : 需要知道的三个点,所属的组,我的姓, 我的名;

             //下边命令行都是通过命令行传入的
            string consulGroup = configuration["ConsulGroup"];
            string ip = configuration["ip"];//这个是启动时传入的;
            int port = Convert.ToInt32(configuration["port"]);

            //把这个当兵的注册到军籍里面
            string serviceId = $"{consulGroup}_{ip}_{port}";

            var check = new AgentServiceCheck //检测客户端是否还在运行
            {
                Interval = TimeSpan.FromSeconds(6),//3s问一次
                HTTP = $"http://{ip}:{port}/heart",//去这个地址访问;
                Timeout = TimeSpan.FromSeconds(2),
                DeregisterCriticalServiceAfter=TimeSpan.FromSeconds(2),//2s 以后就把这个人从军籍里撤销掉.
            };

            //把注册列表填一下
            var regist = new AgentServiceRegistration
            {
                //把我的姓我的名填一下;
                Address = ip,
                Port = port,
                Name = consulGroup,
                ID = serviceId,  //这个功能还不够智能,需要重新组装一下;

                //把检测功能也搞进来:
               // Check = check,
            };
            consul.Agent.ServiceRegister(regist);//保存注册;
        }
    }
2.在静态文件里面,把 consul 的组名穿进去
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConsulGroup": "userservice"
}

3.添加检测是否失效的类:在 Controller 里面 直接 return Ok() 就行
 [Route("[controller]")]
    [ApiController]
    public class HeartController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok();
        }
    }
4.在 startup 类里面,依赖注入,并使用中间件,代码如下:
services.AddSingleton<IConsulClient>(c=>new ConsulClient(cc=>
            {
                cc.Address = new Uri("http://localhost:8500");//ConsulClient 的默认端口
            }
            ));
   public void Configure(IApplicationBuilder app, IWebHostEnvironment env,IConsulClient consul)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthorization();

            app.UseSwagger();
            app.UseSwaggerUI(option=>
            {
                option.SwaggerEndpoint("/swagger/v1/swagger.json","user api"); //第二个名字要和之前的一样。
            } );
            
            app.UseConsul(Configuration,consul);

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }   
5.启动程序

可以使用微服务:
配置好的地址 运行 dotnet UserServer.dll --urls=“http://*:5196” --ip=“127.0.0.1” --port=5196

在运行一个:dotnet UserServer.dll --urls=“http://*:5197” --ip=“127.0.0.1” --port=5197

再运行一个: dotnet UserServer.dll --urls http://*:5001 --ip=“127.0.0.1” --port=5001
此处的 iP 和 port 是必填的,因为命令行启动要用

注意 127.0.0.1是回送地址。在window 系统中还有一个别名 “Localhost”

6.最终效果如下:
  • 有两个 consul 小组: consul:领事
  • userservice小组有两个人:

在这里插入图片描述
userservice小组 里面有了两个人:
在这里插入图片描述

6.2配置了检测以后

按 ctrl + C 。模拟关闭关闭了,如下:

在这里插入图片描述

4.配置网关(Ocelot)

1.引入nuge包

引入 Ocelot 包,使用 15.0.6 ,最新版本有问题。

在Startup文件里进行配置:

  public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            //注入,引用
            services.AddOcelot();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
           //中间件管道引用
            app.UseOcelot();
        }
    }
2.添加配置文件:

  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ReRoutes":[
    {
      "DownstreamPathTemplate": "/{url}", //下端访问的地址
      "DownstreamScheme": "http", //下段访问的方式


      //绕过了consul
      "DownstreamHostAndPorts": [
        {
          "Host": "127.0.0.1",
          "Port": 5001 //下端访问的地址。
        } //可以多个,自动实现负载均衡。
      ],
      "UpstreamPathTemplate": "/{url}", //网管地址。
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ] 
      //上端可以访问的方式。

    }
  ]
3.进行测试

路由输入 6001/test

结果调到了 UserServer 的方法里面

但是此时,OcelotGateway 的中间件管道都已经注销了。
所以此时相当于 Nginx 均衡负载功能。

其实,consul 有点类似于均衡负载的功能,上边只要调用,你下边给我调哪个兵我不管。

4.怎么样调用consul?

引用 nuget 包,观察目录结构:

在这里插入图片描述

更改配置文件:

把原来的给注销掉:


  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ReRoutes": [
    {
      //"DownstreamPathTemplate": "/{url}", //下端访问的地址
      //"DownstreamScheme": "http", //下段访问的方式


      绕过了consul
      //"DownstreamHostAndPorts": [
      //  {
      //    "Host": "127.0.0.1",
      //    "Port": 5001 //下端访问的地址。
      //  } //可以多个,自动实现负载均衡。
      //],
      //"UpstreamPathTemplate": "/{url}", //网管地址。
      //"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], //上端可以访问的方式。


      "DownstreamPathTemplate": "/{url}", //下端访问的地址
      "DownstreamScheme": "http", //下段访问的方式
      "UpstreamPathTemplate": "/{url}", //网管地址。
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], //上端可以访问的方式。
      "ServiceName": "userservice", //哪个consul
      "LoadBalancerOptions": {
        "Type": "RoundRobin" //轮询的方式调用.
      },
      "UserServiceDiscovery": true //是否启用服务发现?当然。
    }
  ],
  //全局配置
  "GlobalConfiguration": { 
    "BaseUrl": "http://127.0.0.1:5001",
    "Host": "loaclhost",//Host 是本地
    "Port": "8500",
    "Type": "Consul"
  }

使用 命令行 启动:

dotnet OcelotGatewag.dll --urls http://*:5001 --ip=“127.0.0.1” --port=6001

注意,最终的启动命令是 url 。–port 只是起到传递参数的作用。

此时在 http:localhost:6001/test,上测试,可以通过。

在这里插入图片描述

5.让每次访问都显示他的网关

如何把网关搞出来,靠依赖注入的方法引入:

 [Route("test")]
    [ApiController]
    public class testController : ControllerBase
    {
        private readonly IConfiguration configuration;

        public testController(IConfiguration configuration)
        {
            this.configuration = configuration;
        }
        public string GetSomething()
        {
            var portss = configuration["port"];
            return $"你好,恭喜你已经进入{portss}了 UserServer 下面的 test 方法";
        }

这样再执行

http://localhost:6001/test

每次刷新得到的结果都不一样:
在这里插入图片描述



在这里插入图片描述
也可以直接输入 swagger界面进行查看
在这里插入图片描述

5.添加第二个服务。

1.新建 ProductService 类

里面的内容和 UserServer 类里面的内容一样,
也是需要添加两个包。
在这里插入图片描述

不同点是:在 appseting 文件里面,添加第二个组名:

只需要更改配置文件,这里也可以看出来使用配置文件的好处。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConsulGroup": "productservice"
}

运行后进入 consul ,能看到有三个组,如下:

在这里插入图片描述

2.配置 第二个服务的Swagger

和第一个一样,比较简单。注意两个 “给人看的名字" ,到底是页面的什么地方。

代码部分:
1.依赖注入的部分。

 option.SwaggerDoc //swaggerDoc 配置文档.

           ("v1", new OpenApiInfo//第一版
            {
               Title = "Product api 大标题",
               Version = "v1", //做个版本好是给人引入的。
            }));

2.中间件管道部分

 app.UseSwagger();//使用Swagger
 app.UseSwaggerUI //使用 Swagger 界面。
                (option =>
                {
                    //第二个名字要和之前的一样。
                    //为什么,应为swagger 会先转换成 JSOn文件,然后再展现出来
     option.SwaggerEndpoint("/swagger/v1/swagger.json", "Product api 中间件管道");//名字
                }
            );


界面如下:


在这里插入图片描述

3.配置 Ocelot

1.再添加一个consul小组

“UpstreamPathTemplate”: “/product/{url}”,

根据定制的规则,可以把下边挂的两个服务找到。


      {
      "DownstreamPathTemplate": "/{url}", //下端访问的地址
      "DownstreamScheme": "http", //下段访问的方式
      "UpstreamPathTemplate": "/user/{url}", //网管地址。
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], //上端可以访问的方式。
      "ServiceName": "userservice", //哪个consul
      "LoadBalancerOptions": {
        "Type": "RoundRobin" //轮询的方式调用.
      },
      "UserServiceDiscovery": true //是否启用服务发现?当然。
    },
    
    {

      "DownstreamPathTemplate": "/{url}", //下端访问的地址
      "DownstreamScheme": "http", //下段访问的方式
      "UpstreamPathTemplate": "/product/{url}", //网管地址。//两个 consul 的时候,就要添加前缀了。
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], //上端可以访问的方式。
      "ServiceName": "productservice", //哪个consul
      "LoadBalancerOptions": {
        "Type": "RoundRobin" //轮询的方式调用.
      },
      "UserServiceDiscovery": true //是否启用服务发现?当然。

结果:

用 consul 测试 product 路由:

在这里插入图片描述
2.测试 user 路由:

在这里插入图片描述
*但是这样配置的swagger,依旧是从,6001的端口号进去的,这样说明配的依然不对,继续更改配置文件。

"Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/swagger/user/swagger.json", //下端访问的地址
      "DownstreamScheme": "http", //下段访问的方式
      "UpstreamPathTemplate": "/user/swagger.json", //网管地址。
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], //上端可以访问的方式。
      "ServiceName": "userservice", //哪个consul
      "UserServiceDiscovery": true //是否启用服务发现?当然。
    },

    {
      "DownstreamPathTemplate": "/user/{url}", //下端访问的地址
      "DownstreamScheme": "http", //下段访问的方式
      "UpstreamPathTemplate": "/user/{url}", //网管地址。
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], //上端可以访问的方式。
      "ServiceName": "userservice", //哪个consul
      "LoadBalancerOptions": {
        "Type": "RoundRobin" //轮询的方式调用.
      },
      "UserServiceDiscovery": true //是否启用服务发现?当然。
    },

    {
      "DownstreamPathTemplate": "/swagger/product/swagger.json", //下端访问的地址
      "DownstreamScheme": "http", //下段访问的方式
      "UpstreamPathTemplate": "/product/swagger.json", //网管地址。//两个 consul 的时候,就要添加前缀了。
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], //上端可以访问的方式。
      "ServiceName": "productservice", //哪个consul
      "UserServiceDiscovery": true //是否启用服务发现?当然。
    }

    ,
    {
      "DownstreamPathTemplate": "/product/{url}", //下端访问的地址
      "DownstreamScheme": "http", //下段访问的方式
      "UpstreamPathTemplate": "/product/{url}", //网管地址。//两个 consul 的时候,就要添加前缀了。
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], //上端可以访问的方式。
      "ServiceName": "productservice", //哪个consul
      "LoadBalancerOptions": {
        "Type": "RoundRobin" //轮询的方式调用.
      },
      "UserServiceDiscovery": true //是否启用服务发现?当然。
    }
  ],
  //全局配置
  "GlobalConfiguration": { 
    "BaseUrl": "http://127.0.0.1:5001",
    "Host": "loaclhost",//Host 是本地
    "Port": "8500",
    "Type": "Consul"
  }
}

以 user 服务为例,
在这里插入图片描述

更改 UserServer 的路由


在这里插入图片描述

ProductServices的路由配置和上边那个一样,就不在多写了,具体看 gitee 源代码。

4.最后实现的结果。

在这里插入图片描述



6.如何防止恶意调用API接口

思路就是:添加 ActionFilter 属性。在每次有请求的时候,都记录一下 请求的 ip 地址和 port 端口号,超过五次的话,就短路。

做个过程也叫做 “熔断机制”

ActionFilter 的添加的方法,执行的顺序
构造函数 => ActionFilter中的 ing 方法 => 函数本身的方法 => ActionFilter 的 ed 方法

具体的添加特性是:

注意两个用法

  • 如何获得 RemoteIpAddress。
  • 如何获得 RemotePort。
  • addMinutes 用法。
  • 短路机制
 //只有继承了 attribute,才能放在别的类前边。
    //只有继承了 ActionFilter的接口,才能实现 Actionfiler的功能。
    public class CtmActionFilterAttribute : Attribute, IActionFilter
    {
        private static Dictionary<string, List<DateTime>> dicRequestTimes
            = new Dictionary<string, List<DateTime>>();
        public void OnActionExecuted(ActionExecutedContext context)
        {
            //throw new NotImplementedException();
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            string ip = context.HttpContext.Connection.RemoteIpAddress.ToString();
            int port = context.HttpContext.Connection.RemotePort;//远程路由的端口号
            string requestname = $"{ip}_{port}";
            //查找是否包含当前地址。
            bool hasHost = dicRequestTimes.ContainsKey(requestname);
            if (hasHost)
            {
                var allTimes = dicRequestTimes[requestname];
                //访问次数为空或者小于五次
                if (allTimes==null||allTimes.Count<5)
                {
                    allTimes.Add(DateTime.Now);
                }
                else
                {
                    //获取当前前一分钟的访问次数。
                    // Now.AddMinutes 想当与是选的时间点。
                    var timeCount = allTimes.Count(m => m>DateTime.Now.AddMinutes(-1));//m在第一分钟以后的。
                    if (timeCount>=5)
                    {
                        //使用断路器.
                        context.Result = new JsonResult("访问过于频繁,请稍候再试") { StatusCode=500};//状态码是500
                    }
                    else
                    {
                        allTimes.Add(DateTime.Now);
                        //把之前一分钟的内容都删掉;
                        var lessCount = allTimes.Count - timeCount;
                        allTimes.RemoveRange(0,lessCount-1);//为节约空间,把一分钟之前的都给删掉。
                    }
                }
            }
            else
            {
                List<DateTime> value = new List<DateTime>();
                value.Add(DateTime.Now);
                dicRequestTimes.Add(requestname,value);
            }
        }
    }

第一次点击:
在这里插入图片描述
连续点击五次以后。

在这里插入图片描述

但是由于每个程序是单独运行的。所以每个实例都会单独的计数,为了消除这种这种问题,一般采用的方法

  • 在网管中进行配置
  • 使用redis 分布式缓存

参考文献:

[1]https://www.bilibili.com/video/BV1pk4y1z7qG/
[2]源码

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Spring Boot 微信小程序的小餐厅点餐系统可以参考以下文献: 1. 《Spring Boot实战》(作者:Craig Walls):这本书介绍了如何使用Spring Boot构建Java应用程序。其中涵盖了Spring Boot的基本概念、开发原理和使用方法,对于基于Spring Boot的微信小程序开发有很好的参考价值。 2. 《微信小程序开发入门》(作者:陈博):这本书介绍了如何开发微信小程序,包括小程序的基本原理、开发环境的搭建、页面和组件的开发等。通过学习这本书,可以了解到如何使用微信小程序开发点餐系统,并与Spring Boot进行整合。 3. 《微信小程序开发指南》(作者:罗波):本书详细介绍了微信小程序的开发过程,包括小程序的基本概念、开发工具的使用、页面布局和样式设计等内容。通过学习本书,可以对微信小程序的开发有更深入的理解,并能够借助Spring Boot构建小餐厅点餐系统。 4. 《深入理解Spring Cloud与微服务构架》(作者:陈雄华):这本书详细介绍了Spring Cloud与微服务架构的相关概念、原理和实践。对于构建基于Spring Boot的微信小程序点餐系统,可以借助Spring Cloud提供的微服务能力,实现分布式的点餐系统架构。 5. 互联网上的相关博客和开源项目:互联网上有很多博客和开源项目分享了基于Spring Boot和微信小程序开发的经验和技巧。可以通过搜索引擎找到这些资源,作为参考学习的素材。 总之,通过阅读相关的文献和资源,深入理解Spring Boot和微信小程序的开发原理和技术栈,可以帮助开发者更好地构建基于Spring Boot的微信小程序点餐系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值