Go/Golang DevOps運維開發(fā)實戰(zhàn)集訓(xùn)營(2023版)
學習地址1:https://pan.baidu.com/s/16vnJqtWw1oZlWAk31uXW6g 提取碼:rl3i?
學習地址2:https://share.weiyun.com/fOjvMvBh 密碼:32sf5s
隨著服務(wù)器、業(yè)務(wù)系統(tǒng)越來越多,已經(jīng)沒有辦法靠人來運維整個平臺和業(yè)務(wù)了??梢栽囅耄绻夹枰斯じ深A(yù)完成工作,那得需要投入多少人力?當業(yè)務(wù)上線時,我們需要部署環(huán)境、部署項目;當發(fā)生問題時,我們?nèi)藶榈厝ジ兄獑栴}后排查問題、定位問題,這時業(yè)務(wù)可能已經(jīng)掛了很長時間。所以要基于對運維的理解構(gòu)建起自動化、智能化運維平臺現(xiàn)階段,掌握一門開發(fā)語言已經(jīng)是高薪運維工程師的必備技能,不會開發(fā),你就不能提高運維工作效率!就不能充分理解公司業(yè)務(wù)流程!就不能幫助調(diào)試、優(yōu)化!
Go是首選的開發(fā)語言,應(yīng)用廣泛;因為它出身名門谷歌,快速吸引大對于DevOps領(lǐng)域來說,批開發(fā)者的關(guān)注和使用,經(jīng)過短短幾年時間,已經(jīng)擠進“開發(fā)語言排行榜”前10名,Go之所以能夠取得如此出色的成績,與他自身特點及發(fā)展密不可分,Go具有語法簡潔、高并發(fā)、跨平臺等優(yōu)勢!
Kubernetes 是一個用于自動化容器化應(yīng)用程序的部署、擴展和管理的系統(tǒng)。它將構(gòu)成應(yīng)用程序的容器分組為邏輯單元,以便于管理和發(fā)現(xiàn)。Kubernetes 可以在不增加運維團隊規(guī)模的情況下擴展和添加無限量的資源。它可以部署在本地,也可以在混合或公共云基礎(chǔ)設(shè)施上運行。
在application.yml文件中,配置MongoDB連接。因為MongoDB自帶數(shù)據(jù)庫連接池,所以我們不需要在Java項目中重復(fù)配置連接池。
spring:
? ……
? data:
? ? mongodb:
? ? ? host: localhost
? ? ? port: 27017
? ? ? database: his
? ? ? #admin是MongoDB用于驗證用戶身份的邏輯庫
? ? ? authentication-database: admin
? ? ? username: admin
? ? ? password: abc123456
因為SpringBoot Data中默認的RedisTemplate存在序列化機制的問題,向Redis里面保存Hash類型數(shù)據(jù)通常是亂碼的,為了解決這個問題,我們需要自己定義配置類,修改RedisTemplate使用的序列化機制。
在com.example.his.api.config包中,創(chuàng)建RedisTemplateConfig類。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisTemplateConfig {
? ? @Bean
? ? public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
? ? ? ? RedisTemplate<Object, Object> template = new RedisTemplate<>();
? ? ? ? template.setKeySerializer(new StringRedisSerializer());
? ? ? ? template.setValueSerializer(new StringRedisSerializer());
? ? ? ? template.setHashKeySerializer(new StringRedisSerializer());
? ? ? ? template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
? ? ? ? template.setConnectionFactory(factory);
? ? ? ? return template;
? ? }
}
因為Controller類用上@RestController注解之后,Web方法返回的對象會被自動轉(zhuǎn)換成JSON對象,所以我們只需要聲明一個封裝類,讓所有Web方法返回這個封裝類的對象即可。除了公共屬性之外,不同的Web方法要返回的業(yè)務(wù)數(shù)據(jù)也不盡相同,所以選擇動態(tài)的結(jié)構(gòu)才是最佳的方案,恰好HashMap允許我們隨便添加數(shù)據(jù),那就選擇HashMap作為父類吧。在com.example.his.api.common包中,創(chuàng)建R.java類。
package com.example.his.api.common;
import org.apache.http.HttpStatus;
import java.util.HashMap;
import java.util.Map;
public class R extends HashMap<String, Object> {
? ? public R() {
? ? ? ? //默認創(chuàng)建的R對象中包含了公共的屬性
? ? ? ? put("code", HttpStatus.SC_OK);
? ? ? ? put("msg", "success");
? ? }
? ? /*
? ? ?* 覆蓋繼承的put函數(shù),添加Key-Value數(shù)據(jù)
? ? ?*/
? ? public R put(String key, Object value) {
? ? ? ? super.put(key, value);
? ? ? ? //把自己返回,用于鏈式調(diào)用
? ? ? ? return this;
? ? }
? ? public static R ok() {
? ? ? ? return new R();
? ? }
? ? public static R ok(String msg) {
? ? ? ? R r = new R();
? ? ? ? r.put("msg", msg);
? ? ? ? return r;
? ? }
? ? public static R ok(Map<String, Object> map) {
? ? ? ? R r = new R();
? ? ? ? r.putAll(map);
? ? ? ? return r;
? ? }
? ? public static R error(int code, String msg) {
? ? ? ? R r = new R();
? ? ? ? r.put("code", code);
? ? ? ? r.put("msg", msg);
? ? ? ? return r;
? ? }
? ? public static R error(String msg) {
? ? ? ? return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
? ? }
? ? public static R error() {
? ? ? ? return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知異常,請聯(lián)系管理員");
? ? }
}
在user.vue頁面中,我們要添加表格標簽和分頁組件標簽,具體如下:
<div>
? ? <el-form :inline="true" :model="dataForm" :rules="dataRule" ref="form">
? ? ? ? ……
? ? </el-form>
? ? <el-table :data="data.dataList"
? ? ? ? :header-cell-style="{'background':'#f5f7fa'}" border
? ? ? ? v-loading="data.loading" @selection-change="selectionChangeHandle">
? ? ? ? <el-table-column type="selection" header-align="center"
? ? ? ? ? ? align="center" width="50" />
? ? ? ? <el-table-column type="index" header-align="center" align="center"
? ? ? ? ? ? width="100" label="序號">
? ? ? ? ? ? <template #default="scope">
? ? ? ? ? ? ? ? <span>{{ (data.pageIndex - 1) * data.pageSize + scope.$index + 1 }}</span>
? ? ? ? ? ? </template>
? ? ? ? </el-table-column>
? ? ? ? <el-table-column prop="name" header-align="center" align="center"
? ? ? ? ? ? min-width="100" label="姓名" />
? ? ? ? <el-table-column prop="sex" header-align="center" align="center"
? ? ? ? ? ? min-width="60" label="性別" />
? ? ? ? <el-table-column prop="tel" header-align="center" align="center"
? ? ? ? ? ? min-width="130" label="電話" />
? ? ? ? <el-table-column prop="email" header-align="center" align="center"
? ? ? ? ? ? min-width="240" label="郵箱" :show-overflow-tooltip="true" />
? ? ? ? <el-table-column prop="hiredate" header-align="center"
? ? ? ? ? ? align="center" min-width="130" label="入職日期" />
? ? ? ? <el-table-column prop="roles" header-align="center" align="center"
? ? ? ? ? ? min-width="150" label="角色" :show-overflow-tooltip="true" />
? ? ? ? <el-table-column prop="dept" header-align="center" align="center"
? ? ? ? ? ? min-width="120" label="部門" />
? ? ? ? <el-table-column prop="status" header-align="center" align="center"
? ? ? ? ? ? min-width="100" label="狀態(tài)" />
? ? ? ? <el-table-column header-align="center" align="center" width="150"
? ? ? ? ? ? label="操作">
? ? ? ? ? ? <template #default="scope">
? ? ? ? ? ? ? ? <el-button type="text"
? ? ? ? ? ? ? ? ? ? v-if="proxy.isAuth(['ROOT', 'USER:UPDATE'])"
? ? ? ? ? ? ? ? ? ? @click="updateHandle(scope.row.id)">
? ? ? ? ? ? ? ? ? ? 修改
? ? ? ? ? ? ? ? </el-button>
? ? ? ? ? ? ? ? <el-button type="text"
? ? ? ? ? ? ? ? ? ? v-if="proxy.isAuth(['ROOT', 'USER:UPDATE'])"
? ? ? ? ? ? ? ? ? ? :disabled="scope.row.status == '離職' || scope.row.root"
? ? ? ? ? ? ? ? ? ? @click="dismissHandle(scope.row.id)">
? ? ? ? ? ? ? ? ? ? 離職
? ? ? ? ? ? ? ? </el-button>
? ? ? ? ? ? ? ? <el-button type="text" :disabled="scope.row.root"
? ? ? ? ? ? ? ? ? ? v-if="proxy.isAuth(['ROOT', 'USER:DELETE'])"
? ? ? ? ? ? ? ? ? ? @click="deleteHandle(scope.row.id)">
? ? ? ? ? ? ? ? ? ? 刪除
? ? ? ? ? ? ? ? </el-button>
? ? ? ? ? ? </template>
? ? ? ? </el-table-column>
? ? </el-table>
? ? <el-pagination @size-change="sizeChangeHandle"
? ? ? ? @current-change="currentChangeHandle" :current-page="data.pageIndex"
? ? ? ? :page-sizes="[10, 20, 50]" :page-size="data.pageSize"
? ? ? ? :total="data.totalCount"
? ? ? ? layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
這定義了一個Go程序中的包。包是Go語言的基本單元,而Go程序本質(zhì)上是一組包。
在我們的示例中,它告訴Go編譯器創(chuàng)建一個可執(zhí)行文件,而不是庫文件。如果你將包命名為不同的名稱,則會遇到以下錯誤:
package command-line-arguments is not a main package
例如,如果你將其重命名為apple,如下所示:
package appleimport "fmt"func main() {
? ? ? ? fmt.Println("Hello, world")
}
如果運行g(shù)o run main.go:
$ go run main.go
package command-line-arguments is not a main package
但是你仍然可以構(gòu)建它,但它不會生成任何可執(zhí)行文件:
$ go build main.go
$ ls
main.go
import { isUsername,isPassword } from '../../utils/validate';
……
function login() {
? ? if (!isUsername(loginForm.username)) {
? ? ? ? proxy.$message({
? ? ? ? ? ? message: '用戶名不正確',
? ? ? ? ? ? type: 'error',
? ? ? ? ? ? duration: 1200
? ? ? ? });
? ? }
? ? else if (!isPassword(loginForm.password)) {
? ? ? ? proxy.$message({
? ? ? ? ? ? message: '密碼不正確',
? ? ? ? ? ? type: 'error',
? ? ? ? ? ? duration: 1200
? ? ? ? });
? ? }
? ? else {
? ? ? ? const data = {
? ? ? ? ? ? username: loginForm.username,
? ? ? ? ? ? password: loginForm.password
? ? ? ? };
? ? ? ? proxy.$http('/mis/user/login', 'POST', data, true, resp => {
? ? ? ? ? ? if (resp.result) {
? ? ? ? ? ? ? ? const permissions = resp.permissions;
? ? ? ? ? ? ? ? const token = resp.token;
? ? ? ? ? ? ? ? //向瀏覽器storage保存令牌和權(quán)限列表
? ? ? ? ? ? ? ? localStorage.setItem('permissions', permissions);
? ? ? ? ? ? ? ? localStorage.setItem('token', token);
? ? ? ? ? ? ? ? //跳轉(zhuǎn)頁面
? ? ? ? ? ? ? ? router.push({ name: 'MisHome' });
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? proxy.$message({
? ? ? ? ? ? ? ? ? ? message: '登陸失敗',
? ? ? ? ? ? ? ? ? ? type: 'error',
? ? ? ? ? ? ? ? ? ? duration: 1200
? ? ? ? ? ? ? ? });
? ? ? ? ? ? }
? ? ? ? });
? ? }
}
接下來,我將把我搭建該技術(shù)框架的入門過程記錄下來:
過程中遇到很多坑,只要硬著頭皮上就沒什么能把你阻擋,讓我們一起go!go!go!
需求:在日常開發(fā)過程中,需要頻繁的修改業(yè)務(wù)邏輯,然后需要部署到服務(wù)器上供QA測試。這個過程對于大多數(shù)開發(fā)人員來說不是難事,但是這個重復(fù)性的過程很浪費開發(fā)人員的時間,我們希望達到,只要我們將改好的業(yè)務(wù)邏輯代碼commit & push,自動部署更新到服務(wù)器上并啟動。就可以測試了。
/*!mycat: sql=
SELECT DISTINCT u.id,
? ? ? ?u.username,
(?
SELECT GROUP_CONCAT( role_name separator "," )?
FROM tb_role?
WHERE JSON_CONTAINS ( u.role, CONVERT (id, CHAR) )?
) AS roles
FROM tb_user u
JOIN tb_role r ON JSON_CONTAINS ( u.role, CONVERT (r.id, CHAR) )
WHERE? u.name="超級管理員"
*/
SELECT DISTINCT u.id,
? ? ? ?u.username,
(?
SELECT GROUP_CONCAT( role_name separator "," )?
FROM tb_role?
WHERE JSON_CONTAINS ( u.role, CONVERT (id, CHAR) )?
) AS roles
FROM tb_user u
JOIN tb_role r ON JSON_CONTAINS ( u.role, CONVERT (r.id, CHAR) )
WHERE? u.name="超級管理員"