月盾的博客

再坚持一下,你的奶茶店马上破产

月盾

1

这两天我看后台后台的时候,发现有几个朋友再问我能不能加盟瑞幸咖啡,能不能赚到钱。

当时我是很迷惑的,因为据我所知瑞幸咖啡从来就没有开放加盟,网上所有挂着瑞幸咖啡加盟旗号的人都是各种沙雕骗子,可能是端幸咖啡。后来我一问,说的不是瑞幸咖啡,是财报上增长亮眼的小鹿茶进行了品牌独立,正在招募各地的合伙人,问我能不能加盟,有没有钱赚。

看得我很伤心,真的。

亏我多次称赞瑞幸是最牛逼的民族品牌,因为从来不割国内的韭菜,都是从国外搞钱烧来补贴大家喝咖啡,走的是公益路线。

没想到浓眉大眼的瑞幸居然搞了小鹿茶现在开始在国内招募合伙人了。

我觉得自己被扇了一耳光。

不谈瑞幸和小鹿茶,单看奶茶加盟这个行业,我觉得我觉得但凡稍微有点眼力或者判断力的人,都不会在2019年投身到奶茶加盟这个无底巨坑了。

作为一个参与了多家奶茶企业及供应链公司融资尽调的人,我想说目前几乎所有的奶茶加盟,对于绝大多数普通人而言,是坑。

加盟奶茶的这些人,90%都是赔钱的,剩下的10%也基本是勉强维持赚不到什么钱,除了奶茶商家外,只有极少数自有店面的人还多少能捞一点点。

如果现在是2014年,那么奶茶还算一个值得玩玩儿的领域。

但现在是2019年,说这个行业是血海都感觉过于保守了。

这个行业的现状简直可以称之为地中海。

不仅深不见底,而且每一个加盟奶茶的人,最终都会愁成地中海。

2

先不说行业,我想先问问各位想要加盟奶茶店的人3个灵魂问题,如果你的答案是否的话,建议直接拉黑所有让你加盟的销售就可以了。

不然你脑子里绝对进奶茶了,还是加浓的那种。

问题1,你在奶茶店打过工吗? 这个问题其实是多个问题的综合,就是你对于奶茶行业了解多少。 你知道客流潮汐吗?你知道近3年店铺人流的变化吗? 你知道原材料的真实成本以及营销成本和售价的ROI关系吗? 你知道如何流程化管理员工让他们不要瞎JB搞甚至偷钱吗? 你知道奶茶标准SOP有多少页,如何抓大放小吗? 你知道如果外送的话,运费和外卖平台促销成本是怎样的吗? 如果你听的很茫然,那么还是别来送人头了,奶茶这个行业,外行杀进来会死的很惨的。

问题2,你在开店当地有资源吗? 这个问题本质上是问你的奶茶店选址成本是否可控,奶茶,是一个高度吃地理位置的行业,这个年代别想什么酒香不怕巷子深,奶茶店地理位置不是人流中心地带的话,直接就不用开了。

各个犄角旮旯里死去的奶茶店数都数不清。

如果是中心地带的话,那么你能保证拿下这块地吗?

注意,拿下的前提是以合理的成本,如果成本失控,那么拿这块地也没意义。

如果不能,你还是别来了,这个门槛对你高到能卡蛋的地步。

问题3,你有多少本钱可以烧掉不心疼?

这个问题本质上是问你的抗风险能力。

我相信每个人做生意都不是为了赔钱的,但正因如此,才要给自己留足余地,因为生意这件事情,真的是说不好的。

别总想着孤注一掷破釜沉舟,人类历史上成功的也就那么几个,绝大多数都是死的不要不要的没人知道,这叫做幸存者偏差。

奶茶店属于非常吃资金流的行业,很多原材料的保质期都不长,而且SOP要求新鲜度,如果没有足够的准备,那么很快你就发现自己手里的钢镚根本吃不住烧。

这时候后悔,就真的晚了。

如果你没有足够的运营资金等着烧,并且烧光了也不影响生活,那么还是别来玩儿奶茶店了,这行业成功率太低。

我给供应链做贷款尽调的时候,见识过的普通家庭创业开奶茶店烧个妻离子散一屁股债的太多了。 多到你想象不到。

3

经历灵魂3问,如果你还是觉得自己想要在奶茶行业一展拳脚,成为一代奶茶大师的话,那么我来和你好好讲讲奶茶加盟里的坑。

其实上述的灵魂3问,各种奶茶加盟都有各种话术应对,无非是跟你讲自己是大品牌,实力雄厚,并且有足够的知名度,毛利率高,并且会帮你选址,帮你安排装修,帮你培训员工,教你如何管理,给你提供标准化的原材料,你只需要一颗创业的心和吃苦的勇气,以及一大堆加盟费就够了, 干奶茶,他们各个都是专业的,你只要躺下数钱就行。

听起来真的很诱人,而且餐饮还是出名的强流水行业,还是实业,干干干,冲冲冲。

各家的话术本质都差不多,但是这些话术中隐藏着一个非常实际的问题。

既然厂家什么东西都能做到位包你上手就赚钱,有你没你差不多,那他们要你干嘛?

一个必然赚钱的店,煞笔才开加盟,你见过星巴克开加盟吗?

实力雄厚,知名度高,且毛利高的厂家差你一个外行的十几万钢镚吗?

他们不是赚钱么?不是管理能力强吗?不是7天培训上岗吗?干嘛不自己做?

除非,他们说的都是假的,就靠你交加盟费来养活。

吃的就是外行不懂。

绝大多数奶茶加盟,本质上就是厂家在割加盟者的韭菜,这个流程已经非常熟练了。

其实也不局限于奶茶,各种品牌加盟的玩法,越来越像资金盘做局了。

先搞几家样板店,然后雇人来买,造成特别火爆的假象,然后择机开放加盟,疯狂打广告,吸引人加盟。

加入前他是儿子般的态度,交钱后他就是爹。

加盟费割一笔,培训费割一笔,然后就是原材料高价割一笔,流水抽成赚一笔,时不时巡店罚款割一笔,最后保证金再割一笔。

你赚不赚不重要,重要的是他们赚了,不仅赚了钱,而且韭菜们开了这么多店,无形中给他们打了很多广告,甚至有的品牌的考核还有拉多少人开店才能返还保证金,彻彻底底的资金盘玩法。

之前在抖音爆火的某茶,一口气搞了6000多家加盟,最火爆的时候,一条街能有3家某案茶,结果3个月不到,倒闭了80%,但这不重要,因为厂家早就赚嗨了,可以换一个新品牌接着割了。

可怜了那些负债加盟的人,以及他们的家庭。

4

再说具体点,当你选择加盟一家奶茶后,会发生什么事情。

记清楚,加盟商的目的不是帮你赚钱,而是从你手上赚钱,这一点非常重要。

首先你得交加盟费,有的品牌收几万块,有的收几千块,随着韭菜不好割了,已经开始有品牌开始0加盟费了,靠后面的运营来赚你钱。

然后你得交保证金,这个不同品牌不一样,但是说法都差不多,这个保证金是约束,防止你做对品牌不利的事情,只要XXX时间没问题或者退出时,保证金会还给你,当然最终还不还给你就不知道了。 培训费和培训之类的洗脑就不说了,我管这套叫做韭菜三板斧。

当你经历了三板斧之后,你需要有一个店铺地址。

这里就考验你拿店的能力了,每个城市,哪怕是县城,那些人流量大的好位置也都是有限的,不要想着找便宜的地方。

奶茶店最重要的三要素,地理位置,地理位置,还是地理位置,就算你打算做外卖,也要选附近有学校或者是CBD。

nestjs框架中使用nunjucks模板引擎

月盾

main.ts

import { NestFactory } from '@nestjs/core';
import {
    ExpressAdapter,
    NestExpressApplication,
} from '@nestjs/platform-express';
import { AppModule } from './app.module';
import nunjucks = require('nunjucks');
import { join } from 'path';

async function bootstrap() {
    const app = await NestFactory.create<NestExpressApplication>(
        AppModule,
        new ExpressAdapter(),
    );
    app.useStaticAssets(join(__dirname, '..', 'public')); // NestFactory.create需要加泛型参数<NestExpressApplication>
    app.setBaseViewsDir(join(__dirname, '..', 'views')); // 修改模板文件后立马生效,否则需要重启服务,nunjucks watch参数也有相同作用
    nunjucks.configure('views', {
        ext:'njk',
        autoescape: true,
        express: app,
        watch: true,
    });
    await app.listen(3000, () => {
    });
}
bootstrap();

app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
    constructor(private readonly appService: AppService) {}

    @Get('articles')
    // @Render('articles.njk') // 不能使用@Render装饰器,而是使用res.render
    async findArticlesByUser(@Res() res:Response): Promise<any> {
        return res.render('articles.njk', {
            title: "标题",
            articles
        })
    }
}

articles.njk

Consul 入门教程

月盾

一、什么是服务发现

二、consul 简介

三、consul的几个概念

四、安装 Consul

五、运行 Consul Agent

六、集群成员

七、停止 Agent

八、注册服务

九、Consul 集群

十、健康检查

十一、KV Data

十二、Consul Web UI

十三、Docker下安装consul

作者:菲宇
来源:CSDN
原文:https://blog.csdn.net/bbwangj/article/details/81116505

服务发现:Zookeeper vs etcd vs Consul

月盾

【编者的话】本文对比了Zookeeper、etcd和Consul三种服务发现工具,探讨了最佳的服务发现解决方案,仅供参考。

如果使用预定义的端口,服务越多,发生冲突的可能性越大,毕竟,不可能有两个服务监听同一个端口。管理一个拥挤的比方说被几百个服务所使用的所有端口的列表,本身就是一个挑战,添加到该列表后,这些服务需要的数据库和数量会日益增多。因此我们应该部署无需指定端口的服务,并且让Docker为我们分配一个随机的端口。唯一的问题是我们需要发现端口号,并且让别人知道。

当我们开始在一个分布式系统上部署服务到其中一台服务器上时,事情会变得更加复杂,我们可以选择预先定义哪台服务器运行哪个服务的方式,但这会导致很多问题。我们应该尽我们所能尽量利用服务器资源,但是如果预先定义每个服务的部署位置,那么要实现尽量利用服务器资源是几乎不可能的。另一个问题是服务的自动伸缩将会非常困难,更不用说自动恢复了,比方说服务器故障。另一方面,如果我们将服务部署到某台只有最少数量的容器在运行的服务器上,我们需要添加IP地址到数据列表中,这些数据需要可以被发现并存储在某处。

当我们需要存储和发现一些与正在工作的服务相关的信息时,还有很多其他的例子。

为了能够定位服务,我们需要至少接下来的两个有用的步骤。 服务注册——该步骤存储的信息至少包括正在运行的服务的主机和端口信息 服务发现——该步骤允许其他用户可以发现在服务注册阶段存储的信息。

除了上述的步骤,我们还需要考虑其他方面。如果一个服务停止工作并部署/注册了一个新的服务实例,那么该服务是否应该注销呢?当有相同服务的多个副本时咋办?我们该如何做负载均衡呢?如果一个服务器宕机了咋办?所有这些问题都与注册和发现阶段紧密关联。现在,我们限定只在服务发现的范围里(常见的名字,围绕上述步骤)以及用于服务发现任务的工具,它们中的大多数采用了高可用的分布式键/值存储。

服务发现工具

服务发现工具的主要目标是用来服务查找和相互对话,为此该工具需要知道每个服务,这不是一个新概念,在Docker之前就已经存在很多类似的工具了,然而,容器带给了这些工具一个全新水平的需求。

服务发现背后的基本思想是对于服务的每一个新实例(或应用程序),能够识别当前环境和存储相关信息。存储的注册表信息本身通常采用键/值对的格式,由于服务发现经常用于分布式系统,所以要求这些信息可伸缩、支持容错和分布式集群中的所有节点。这种存储的主要用途是给所有感兴趣的各方提供最起码诸如服务IP地址和端口这样的信息,用于它们之间的相互通讯,这些数据还经常扩展到其它类型的信息服务发现工具倾向于提供某种形式的API,用于服务自身的注册以及服务信息的查找。

比方说我们有两个服务,一个是提供方,另一个是第一个服务的消费者,一旦部署了服务提供方,就需要在服务发现注册表中存储其信息。接着,当消费者试图访问服务提供者时,它首先查询服务注册表,使用获取到的IP地址和端口来调用服务提供者。为了与注册表中的服务提供方的具体实现解耦,我们常常采用某种代理服务。这样消费者总是向固定IP地址的代理请求信息,代理再依次使用服务发现来查找服务提供方信息并重定向请求,在本文中我们稍后通过反向代理来实现。现在重要的是要理解基于三种角色(服务消费者、提供者和代理)的服务发现流程。

服务发现工具要查找的是数据,至少我们应该能够找出服务在哪里?服务是否健康和可用?配置是什么样的?既然我们正在多台服务器上构建一个分布式系统,那么该工具需要足够健壮,保证其中一个节点的宕机不会危及数据,同时,每个节点应该有完全相同的数据副本,进一步地,我们希望能够以任何顺序启动服务、杀死服务或者替换服务的新版本,我们还应该能够重新配置服务并且查看到数据相应的变化。

让我们看一下一些常用的选项来完成我们上面设定的目标。

手动配置

大多数服务仍然是需要手动管理的,我们预先决定在何处部署服务、如何配置和希望不管什么原因,服务都将继续正常工作,直到天荒地老。这样的目标不是可以轻易达到的。部署第二个服务实例意味着我们需要启动全程的手动处理,我们需要引入一台新的服务器,或者找出哪一台服务器资源利用率较低,然后创建一个新的配置集并启动服务。情况或许会变得越来越复杂,比方说,硬件故障导致的手动管理下的反应时间变得很慢。可见性是另外一个痛点,我们知道什么是静态配置,毕竟是我们预先准备好的,然而,大多数的服务有很多动态生成的信息,这些信息不是轻易可见的,也没有一个单独的地方供我们在需要时参考这些数据。

反应时间会不可避免的变慢,鉴于存在许多需要手动处理的移动组件,故障恢复和监控也会变得非常难以管理。

尽管在过去或者当服务/服务器数量很少的时候有借口不做这项工作,随着服务发现工具的出现,这个借口已经不存在了。

Zookeeper

Zookeeper是这种类型的项目中历史最悠久的之一,它起源于Hadoop,帮助在Hadoop集群中维护各种组件。它非常成熟、可靠,被许多大公司(YouTube、eBay、雅虎等)使用。其数据存储的格式类似于文件系统,如果运行在一个服务器集群中,Zookeper将跨所有节点共享配置状态,每个集群选举一个领袖,客户端可以连接到任何一台服务器获取数据。

Zookeeper的主要优势是其成熟、健壮以及丰富的特性,然而,它也有自己的缺点,其中采用Java开发以及复杂性是罪魁祸首。尽管Java在许多方面非常伟大,然后对于这种类型的工作还是太沉重了,Zookeeper使用Java以及相当数量的依赖使其对于资源竞争非常饥渴。因为上述的这些问题,Zookeeper变得非常复杂,维护它需要比我们期望从这种类型的应用程序中获得的收益更多的知识。这部分地是由于丰富的特性反而将其从优势转变为累赘。应用程序的特性功能越多,就会有越大的可能性不需要这些特性,因此,我们最终将会为这些不需要的特性付出复杂度方面的代价。

Zookeeper为其他项目相当大的改进铺平了道路,“大数据玩家“在使用它,因为没有更好的选择。今天,Zookeeper已经老态龙钟了,我们有了更好的选择。

etcd

etcd是一个采用HTTP协议的健/值对存储系统,它是一个分布式和功能层次配置系统,可用于构建服务发现系统。其很容易部署、安装和使用,提供了可靠的数据持久化特性。它是安全的并且文档也十分齐全。

etcd比Zookeeper是比更好的选择,因为它很简单,然而,它需要搭配一些第三方工具才可以提供服务发现功能。

现在,我们有一个地方来存储服务相关信息,我们还需要一个工具可以自动发送信息给etcd。但在这之后,为什么我们还需要手动把数据发送给etcd呢?即使我们希望手动将信息发送给etcd,我们通常情况下也不会知道是什么信息。记住这一点,服务可能会被部署到一台运行最少数量容器的服务器上,并且随机分配一个端口。理想情况下,这个工具应该监视所有节点上的Docker容器,并且每当有新容器运行或者现有的一个容器停止的时候更新etcd,其中的一个可以帮助我们达成目标的工具就是Registrator。

Registrator

Registrator通过检查容器在线或者停止运行状态自动注册和去注册服务,它目前支持etcd、Consul和SkyDNS 2。

Registrator与etcd是一个简单但是功能强大的组合,可以运行很多先进的技术。每当我们打开一个容器,所有数据将被存储在etcd并传播到集群中的所有节点。我们将决定什么信息是我们的。

上述的拼图游戏还缺少一块,我们需要一种方法来创建配置文件,与数据都存储在etcd,通过运行一些命令来创建这些配置文件。

Confd

Confd是一个轻量级的配置管理工具,常见的用法是通过使用存储在etcd、consul和其他一些数据登记处的数据保持配置文件的最新状态,它也可以用来在配置文件改变时重新加载应用程序。换句话说,我们可以用存储在etcd(或者其他注册中心)的信息来重新配置所有服务。

对于etcd、Registrator和Confd组合的最后的思考 当etcd、Registrator和Confd结合时,可以获得一个简单而强大的方法来自动化操作我们所有的服务发现和需要的配置。这个组合还展示了“小”工具正确组合的有效性,这三个小东西可以如我们所愿正好完成我们需要达到的目标,若范围稍微小一些,我们将无法完成我们面前的目标,而另一方面如果他们设计时考虑到更大的范围,我们将引入不必要的复杂性和服务器资源开销。

在我们做出最后的判决之前,让我们看看另一个有相同目标的工具组合,毕竟,我们不应该满足于一些没有可替代方案的选择。

Consul

Consul是强一致性的数据存储,使用gossip形成动态集群。它提供分级键/值存储方式,不仅可以存储数据,而且可以用于注册器件事各种任务,从发送数据改变通知到运行健康检查和自定义命令,具体如何取决于它们的输出。

与Zookeeper和etcd不一样,Consul内嵌实现了服务发现系统,所以这样就不需要构建自己的系统或使用第三方系统。这一发现系统除了上述提到的特性之外,还包括节点健康检查和运行在其上的服务。

Zookeeper和etcd只提供原始的键/值队存储,要求应用程序开发人员构建他们自己的系统提供服务发现功能。而Consul提供了一个内置的服务发现的框架。客户只需要注册服务并通过DNS或HTTP接口执行服务发现。其他两个工具需要一个亲手制作的解决方案或借助于第三方工具。

Consul为多种数据中心提供了开箱即用的原生支持,其中的gossip系统不仅可以工作在同一集群内部的各个节点,而且还可以跨数据中心工作。

Consul还有另一个不错的区别于其他工具的功能,它不仅可以用来发现已部署的服务以及其驻留的节点信息,还通过HTTP请求、TTLs(time-to-live)和自定义命令提供了易于扩展的健康检查特性。

Registrator

Registrator有两个Consul协议,其中consulkv协议产生类似于etcd协议的结果。

除了通常的IP和端口存储在etcd或consulkv协议中之外,Registrator consul协议存储了更多的信息,我们可以得到服务运行节点的信息,以及服务ID和名称。我们也可以借助于一些额外的环境变量按照一定的标记存储额外的信息。

Consul-template

confd可以像和etce搭配一样用于Consul,不过Consul有自己的模板服务,其更适配Consul。

通过从Consul获得的信息,Consul-template是一个非常方便的创建文件的途径,还有一个额外的好处就是在文件更新后可以运行任意命令,正如confd,Consul-template也可以使用 Go模板格式。

Consul健康检查、Web界面和数据中心

监控集群节点和服务的健康状态与测试和部署它们一样的重要。虽然我们应该向着拥有从来没有故障的稳定的环境努力,但我们也应该承认,随时会有意想不到的故障发生,时刻准备着采取相应的措施。例如我们可以监控内存使用情况,如果达到一定的阈值,那么迁移一些服务到集群中的另外一个节点,这将是在发生“灾难”前执行的一个预防措施。另一方面,并不是所有潜在的故障都可以被及时检测到并采取措施。单个服务可能会齿白,一个完整的节点也可能由于硬件故障而停止工作。在这种情况下我们应该准备尽快行动,例如一个节点替换为一个新的并迁移失败的服务。Consul有一个简单的、优雅的但功能强大的方式进行健康检查,当健康阀值达到一定数目时,帮助用户定义应该执行的操作。

如果用户Google搜索“etcd ui”或者“etec dashboard”时,用户可能看到只有几个可用的解决方案,可能会问为什么我们还没有介绍给用户,这个原因很简单,etcd只是键/值对存储,仅此而已。通过一个UI呈现数据没有太多的用处,因为我们可以很容易地通过etcdctl获得这些数据。这并不意味着etcd UI是无用的,但鉴于其有限的使用范围,它不会产生多大影响。

Consu不仅仅是一个简单的键/值对存储,正如我们已经看到的,除了存储简单的键/值对,它还有一个服务的概念以及所属的数据。它还可以执行健康检查,因此成为一个好的候选dashboard,在上面可以看到我们的节点的状态和运行的服务。最后,它支持了多数据中心的概念。所有这些特性的结合让我们从不同的角度看到引入dashboard的必要性。

通过Consul Web界面,用户可以查看所有的服务和节点、监控健康检查状态以及通过切换数据中心读取设置键/值对数据。

对于Consul、Registrator、Template、健康检查和Web UI的最终思考

Consul以及上述我们一起探讨的工具在很多情况下提供了比etcd更好的解决方案。这是从内心深处为了服务架构和发现而设计的方案,简单而强大。它提供了一个完整的同时不失简洁的解决方案,在许多情况下,这是最佳的服务发现以及满足健康检查需求的工具。 结论 所有这些工具都是基于相似的原则和架构,它们在节点上运行,需要仲裁来运行,并且都是强一致性的,都提供某种形式的键/值对存储。

Zookeeper是其中最老态龙钟的一个,使用年限显示出了其复杂性、资源利用和尽力达成的目标,它是为了与我们评估的其他工具所处的不同时代而设计的(即使它不是老得太多)。

etcd、Registrator和Confd是一个非常简单但非常强大的组合,可以解决大部分问题,如果不是全部满足服务发现需要的话。它还展示了我们可以通过组合非常简单和特定的工具来获得强大的服务发现能力,它们中的每一个都执行一个非常具体的任务,通过精心设计的API进行通讯,具备相对自治工作的能力,从架构和功能途径方面都是微服务方式。

Consul的不同之处在于无需第三方工具就可以原生支持多数据中心和健康检查,这并不意味着使用第三方工具不好。实际上,在这篇博客里我们通过选择那些表现更佳同时不会引入不必要的功能的的工具,尽力组合不同的工具。使用正确的工具可以获得最好的结果。如果工具引入了工作不需要的特性,那么工作效率反而会下降,另一方面,如果工具没有提供工作所需要的特性也是没有用的。Consul很好地权衡了权重,用尽量少的东西很好的达成了目标。

Consul使用gossip来传播集群信息的方式,使其比etcd更易于搭建,特别是对于大的数据中心。将存储数据作为服务的能力使其比etcd仅仅只有健/值对存储的特性更加完整、更有用(即使Consul也有该选项)。虽然我们可以在etcd中通过插入多个键来达成相同的目标,Consul的服务实现了一个更紧凑的结果,通常只需要一次查询就可以获得与服务相关的所有数据。除此之外,Registrator很好地实现了Consul的两个协议,使其合二为一,特别是添加Consul-template到了拼图中。Consul的Web UI更是锦上添花般地提供了服务和健康检查的可视化途径。

我不能说Consul是一个明确的赢家,而是与etcd相比其有一个轻微的优势。服务发现作为一个概念,以及作为工具都很新,我们可以期待在这一领域会有许多的变化。秉承开放的心态,大家可以对本文的建议持保留态度,尝试不同的工具然后做出自己的结论。

热水器维修

月盾

天然气热水器莫名其妙就不打火了,然后到58上找了维修公司,在电话里问维修多少钱就是死活不说,连区间都不说。然后就是维修员上门检修20元。 就换了这么个零件,看着也不是特别精密的零件,问师傅换这个多少钱,说是300元。到最后收费的时候说是380,这两个数字听起来很像吗?我能听错!!还要加50元人工费,合计450元。 或许这就是维修公司的套路吧,开始不说多少钱,总是催着你什么时候可以上门维修,维修的时候又说这坏那坏了,换个零件又很贵。网上一看二手的都不过300块,还不及一个零件贵。还有就是问价格的时候一定要多次确认多少钱后再决定是否需要更换。最后要保留维修票据,以免日后保修。

部署golang到服务器

月盾

说起将开发好的程序部署到服务上,常用的有两种方式:

  1. 本地编译打包,上传到服务器
  2. git push到远程仓库,在服务器上拉取(编译-打包)

无论以怎样的方式发布,都只有熟悉流程才能得心应手。今天我要说的是golang的部署流程。

如果是在公司内,自然有专人负责发布事宜,也有公司暂无运维人员,这时还是由开发人员负责服务器发布工作,当然,CI/CD这类工具一般也没有搭建起来。但这并不影响我们快速发布。 得益于go的编译速度,整个发布过程可能也就2分钟,接下来说明一下我个人的发布流程:

  1. 在项目目录下执行go打包命令
GOOS=linux GOARCH=amd64 go build 

由于是要部署到Linux服务器上,所以加上GOOS=linux GOARCH=amd64就可以打包出对应系统的二进制可执行文件。可以将该命令写成脚本文件。

  1. 推送代码到git仓库,这一步并不是必须,之所以需要这一步,是因为go只打包*.go文件,并不会打包静态文件,所以还需要把相关静态文件推送的git仓库以便拉取。

  2. 上传打包好的二进制可执行文件到服务器的项目目录下。为什么是项目目录?因为还有静态文件需要使用,所以服务器上也要有同样的项目结构。可借助一些工具来上传,我使用了rz命令来上传。

  3. git pull代码,主要是拉取静态文件。

  4. 重启应用。

整个过程比较耗时的操作是上传文件和推拉代码,打包和重启应用反而很快,基本是两三秒完成。 golang相对于其他语言,在服务,器上不需要安装运行时,不像Java和nodejs都需要安装正确的运行时版本,go只需要把打包好的二进制可执行文件扔上去就可以执行。

nodejs-go内存占比

月盾

同一台服务器上部署了两个功能差不多的服务,但是内存占比差距有点大。 nodejs-go内存占比 go占14.7M nodejs占122.2M

go语言开发grpc之安装grpc

月盾

一、安装gRPC

$ go get -u google.golang.org/grpc  
package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.)

grpc的源码库迁移到了github上,所以需要手动下载了。grpc-go 正常情况下按照以下方式就可安装完成

git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc

git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net

git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text

go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

cd $GOPATH/src/

go install google.golang.org/grpc

但是在某些情况可能连git clone都不行。就像下面这样的:

gRPC负载均衡

月盾

gRPC是谷歌开发的跨语言(C, C++, Python, PHP, Nodejs, C#, Objective-C、Golang、Java)RPC框架,跨语言是指可以使用gRPC进行个语言之间的通信,例如:PHP可以对java进行远程调用。

在系统架构中,我们会把多个系统公共的模块拆分出来做成单独的服务,可以提供RESTful接口,也可以为了低延迟快速响应而提供RPC接口。如果选择的是gRPC,上线后发现多个系统都请求这个RPC服务提供者,而且流量很大的时候负载过高导致崩溃。为了降低负载和提高可用性,理所当然的要做集群,用nginx作为代理服务器,幸运的是nginx版本为1.13及以上支持了gRPC的负载均衡。那么请看以下配置:

upstream grpcservice {
    server localhost:50051;
    server localhost:50052;
}
server {
    listen       8080  http2;#需要加http2
    server_name  localhost;

    location / {
        grpc_pass grpc://grpcservice;#以grpc为前缀
    }

    grpc_connect_timeout 10;
}

配置好nginx以后,客户端需要连接到localhost:8080来调用远程服务。 效果图: grpc-nginx

可以看到,一次任务的多个请求两个RPC服务器都有输出,证明请求被分配到了两台服务器上。

虽然我们使用了两台服务器来保证性能和可用性,但是当其中一台服务器挂掉以后发现部分请求响应非常慢。

grpc_nginx2

原因是服务器虽然宕机,但是请求还会发送到挂掉的服务器上,然后等待超时(默认1分钟),超时后再请求另外的服务器,重新请求以后可能还会再次分配到这台宕机的服务器。为了能加快响应,配置了grpc_connect_timeout选项,把时间设为5秒,再次测试,大概5秒后就能返回。如果设置更小的时间响应时间会更短。

grpc_nginx3

linux修改MySQL 5.7.22字符集为utf8

月盾

网上也找了很多方案结果就是奇葩的不成功,最后直接修改/etc/mysql/mysql.conf.d/mysqld.cnf成功了。 在该文件最后添加

default-storage-engine=INNODB
character-set-server=utf8
collation-server=utf8_general_ci

重启成功。

这是本地虚拟机里的mysql

mysql> show variables like "character%";
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

完全正确,但是修改阿里云服务器以后还是有点小问题

character_set_database这一项还是latin1,不过似乎并不影响。 友情提示:纠正了mysql字符集发现新建的表还是latin1字符集,那有可能是该数据库本身的字符集就不对,可以试着看看。