山西省:發佈《加快推進電動汽車產業發展和推廣應用的實施意見(公開徵求意見稿)》通知

為加快推進電動汽車產業發展和推廣應用,山西省擬於近期出臺《加快推進電動汽車產業發展和推廣應用的實施意見》(以下簡稱《實施意見》)。為保障公民、法人和其他社會組織的知情權、參與權和監督權,做到依法決策、科學決策、民主決策,現將《實施意見(公開徵求意見稿)》公佈,廣泛徵求社會各界意見,敬請提出寶貴意見和建議,於2015年11月18日24:00時前,通過電子郵件發送至郵箱:

政策原文:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

webpack 打包優化的四種方法(多進程打包,多進程壓縮,資源 CDN,動態 polyfill)

如今,webpack 毫無疑問是前端構建領域里最耀眼的一顆星,無論你前端走哪條路線,都需要有很強的webpack 知識。webpack 的基本用法這裏就不展開講了。主要探討一下如何提高 webpack 的打包速度。

這篇文章以 vue cli3.0+webpack4.0+nodejs10.0+ 這幾個版本為例。

一、打包分析

1.1、速度分析

我們的目的是優化打包速度,那肯定需要一個速度分析插件,此時 speed-measure-webpack-plugin 就派上用場了。它的作用如下:

  • 分析整個打包總耗時
  • 每個 pluginloader 的耗時情況

首先,安裝插件

npm i -D speed-measure-webpack-plugin

然後修改 vue.config.js 配置文件 (vuecli3+ 版本的配置文件統一在這個文件里修改,如果沒有該文件,在根目錄新建一個)

// 導入速度分析插件
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");

// 實例化插件
const smp = new SpeedMeasurePlugin();

module.exports = {
    configureWebpack: smp.wrap({
        plugins: [
            // 這裡是自己項目里需要使用到的其他插件
            new yourOtherPlugin()
        ]
    })
}

運行打包命令之後,可以看到,打包總耗時為 2min,51.99s

1.2、體積分析

分析完打包速度之後,接着我們來分析打包之後每個文件以及每個模塊對應的體積大小。使用到的插件為 webpack-bundle-analyzer,構建完成後會在 8888 端口展示大小。

首先,安裝插件

npm i -D webpack-bundle-analyzer

修改 vue.config.js 配置文件

// 導入速度分析插件
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");

// 導入體積分析插件
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;

// 實例化速度分析插件
const smp = new SpeedMeasurePlugin();

module.exports = {
    configureWebpack: smp.wrap({
        plugins: [
            // 實例化體積分析插件
            new BundleAnalyzerPlugin()
        ]
    })
}

構建之後可以看到,其中黃色塊 chunk-vendors 文件佔比最大,為 1.34MB

二、打包優化

2.1、多進程多實例構建,資源并行解析

多進程構建的方案比較知名的有以下三個:

  • thread-loader (推薦使用這個)
  • parallel-webpack
  • HappyPack

這裏以 thread-loader 為例配置多進程多實例構建
安裝 loader

npm i -D thread-loader

接下來在 vue.config.js 配置文件中使用該 loader

// 導入速度分析插件
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");

// 實例化插件
const smp = new SpeedMeasurePlugin();
module.exports = {
    configureWebpack: smp.wrap({
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: ['thread-loader']
                }
            ]
        }
    })
}

然後看下構建花費的時間, 2min,49.21s,相較於之前提升了 5s

2.2、公用代碼提取,使用 CDN 加載

對於vue,vuex,vue-router,axios,echarts,swiper等我們可以利用webpack的externals參數來配置,這裏我們設定只需要在生產環境中才需要使用。
下面配置 vue.config.js

// 導入速度分析插件
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");

// 導入體積分析插件
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;

//判斷是否為生產環境
const isProduction = process.env.NODE_ENV === 'production';

//定義 CDN 路徑,這裏採用 bootstrap 的 cdn
const cdn = {
    css: [
        'https://cdn.bootcss.com/Swiper/4.5.1/css/swiper.min.css'
    ],
    js: [
        'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',
        'https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js',
        'https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js',
        'https://cdn.bootcss.com/axios/0.19.0/axios.min.js',
        'https://cdn.bootcss.com/echarts/4.3.0/echarts.min.js',
        'https://cdn.bootcss.com/Swiper/4.5.1/js/swiper.min.js',
    ]
}

// 實例化插件
const smp = new SpeedMeasurePlugin();
module.exports = {
    chainWebpack: config => {
        // 生產環境配置
        if (isProduction) {
            // 生產環境注入 cdn
            config.plugin('html')
                .tap(args => {
                    args[0].cdn = cdn;
                    return args;
                });
        }
    },
    configureWebpack: smp.wrap({
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: ['thread-loader']
                }
            ]
        },
        plugins: [
            new BundleAnalyzerPlugin()
        ],
        //生產環境注入 cdn
        externals: isProduction && {
            'vue': 'Vue',
            'vuex': 'Vuex',
            'vue-router': 'VueRouter',
            'axios': 'axios',
            'echarts': 'echarts',
            'swiper': 'Swiper'
        } || {}
    })
}

緊接着,改造 html 頁面。用於讓我們配置的 cdn 路徑注入到 html 頁面

<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <!-- 使用 CDN 的 CSS 文件 -->
  <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
    <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
  <% } %>
</head>

<body>
  <noscript>
    <strong>We're sorry but eye-admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>
  <!-- built files will be auto injected -->

 <!-- 使用 CDN 的 JS 文件 -->
  <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
    <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
  <% } %>
</body>

</html>

最重要的一步,看下效果,可以看到現在耗時 1min,39.19s,整整提升了 1 分多鍾!

2.3、多進程多實例并行壓縮

并行壓縮主流有以下三種方案

  • 使用 parallel-uglify-plugin 插件
  • uglifyjs-webpack-plugin 開啟 parallel 參數
  • terser-webpack-plugin 開啟 parallel 參數 (推薦使用這個,支持 ES6 語法壓縮)

安裝插件依賴

npm i -D terser-webpack-plugin

接下來在 vue.config.js 配置文件中使用插件,最終的配置文件如下

// 導入速度分析插件
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");

// 導入代碼壓縮插件
const TerserPlugin = require("terser-webpack-plugin");

// 導入體積分析插件
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;

//判斷是否為生產環境
const isProduction = process.env.NODE_ENV === 'production';

//定義 CDN 路徑,這裏採用 bootstrap 的 cdn
const cdn = {
    css: [
        'https://cdn.bootcss.com/Swiper/4.5.1/css/swiper.min.css'
    ],
    js: [
        'https://cdn.bootcss.com/vue/2.6.10/vue.min.js',
        'https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js',
        'https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js',
        'https://cdn.bootcss.com/axios/0.19.0/axios.min.js',
        'https://cdn.bootcss.com/echarts/4.3.0/echarts.min.js',
        'https://cdn.bootcss.com/Swiper/4.5.1/js/swiper.min.js',
    ]
}

// 實例化插件
const smp = new SpeedMeasurePlugin();
module.exports = {
    chainWebpack: config => {
        // 生產環境配置
        if (isProduction) {
            // 生產環境注入 cdn
            config.plugin('html')
                .tap(args => {
                    args[0].cdn = cdn;
                    return args;
                });
        }
    },
    configureWebpack: smp.wrap({
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: ['thread-loader']
                }
            ]
        },
        plugins: [
            new BundleAnalyzerPlugin()
        ],
        //生產環境注入 cdn
        externals: isProduction && {
            'vue': 'Vue',
            'vuex': 'Vuex',
            'vue-router': 'VueRouter',
            'axios': 'axios',
            'echarts': 'echarts',
            'swiper': 'Swiper'
        } || {},
        optimization: {
            minimizer: [
                new TerserPlugin({
                    parallel: 4
                })
            ]
        }
    })
}

2.4、使用 polyfill 動態服務

動態 polyfill 指的是根據不同的瀏覽器,動態載入需要的 polyfillPolyfill.io 通過嘗試使用 polyfill 重新創建缺少的功能,可以更輕鬆地支持不同的瀏覽器,並且可以大幅度的減少構建體積。

Polyfill Service 原理

識別 User Agent,下發不同的 Polyfill

使用方法:在 index.html 中引入如下 script 標籤

<script crossorigin="anonymous" src=""></script>

三、完結

At last,看完之後有什麼不懂的,可以留言反饋。

轉載請註明出處:
作者:TSY
個人空間:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※評比南投搬家公司費用收費行情懶人包大公開

009.Kubernetes二進制部署kube-apiserver

一 部署master節點

1.1 master節點服務


kubernetes master 節點運行如下組件:

  • kube-apiserver
  • kube-scheduler
  • kube-controller-manager
  • kube-nginx


kube-apiserver、kube-scheduler 和 kube-controller-manager 均以多實例模式運行:

kube-scheduler 和 kube-controller-manager 會自動選舉產生一個 leader 實例,其它實例處於阻塞模式,當 leader 掛了后,重新選舉產生新的 leader,從而保證服務可用性。

kube-apiserver 是無狀態的,需要通過 kube-nginx 進行代理訪問,從而保證服務可用性。

1.2 安裝Kubernetes

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# wget https://dl.k8s.io/v1.14.2/kubernetes-server-linux-amd64.tar.gz
  3 [root@k8smaster01 work]# tar -xzvf kubernetes-server-linux-amd64.tar.gz
  4 [root@k8smaster01 work]# cd kubernetes
  5 [root@k8smaster01 kubernetes]# tar -xzvf  kubernetes-src.tar.gz


1.3 分發Kubernetes

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     scp kubernetes/server/bin/{apiextensions-apiserver,cloud-controller-manager,kube-apiserver,kube-controller-manager,kube-proxy,kube-scheduler,kubeadm,kubectl,kubelet,mounter} root@${master_ip}:/opt/k8s/bin/
  7     ssh root@${master_ip} "chmod +x /opt/k8s/bin/*"
  8   done


二 部署高可用kube-apiserver

2.1 高可用apiserver介紹


本實驗部署一個三實例 kube-apiserver 集群的步驟,它們通過 kube-nginx 進行代理訪問,從而保證服務可用性。

2.2 創建Kubernetes證書和私鑰

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# cat > kubernetes-csr.json <<EOF
  3 {
  4   "CN": "kubernetes",
  5   "hosts": [
  6     "127.0.0.1",
  7     "172.24.8.71",
  8     "172.24.8.72",
  9     "172.24.8.73",
 10     "${CLUSTER_KUBERNETES_SVC_IP}",
 11     "kubernetes",
 12     "kubernetes.default",
 13     "kubernetes.default.svc",
 14     "kubernetes.default.svc.cluster",
 15     "kubernetes.default.svc.cluster.local."
 16   ],
 17   "key": {
 18     "algo": "rsa",
 19     "size": 2048
 20   },
 21   "names": [
 22     {
 23       "C": "CN",
 24       "ST": "Shanghai",
 25       "L": "Shanghai",
 26       "O": "k8s",
 27       "OU": "System"
 28     }
 29   ]
 30 }
 31 EOF
 32 #創建Kubernetes的CA證書請求文件



解釋:

hosts 字段指定授權使用該證書的 IP 和域名列表,這裏列出了 master 節點 IP、kubernetes 服務的 IP 和域名;

kubernetes 服務 IP 是 apiserver 自動創建的,一般是 –service-cluster-ip-range 參數指定的網段的第一個IP,後續可以通過下面命令獲取:

  1 # kubectl get svc kubernetes




  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
  3 -ca-key=/opt/k8s/work/ca-key.pem -config=/opt/k8s/work/ca-config.json \
  4 -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes	#生成CA密鑰(ca-key.pem)和證書(ca.pem)


2.3 分發證書和私鑰

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     ssh root@${master_ip} "mkdir -p /etc/kubernetes/cert"
  7     scp kubernetes*.pem root@${master_ip}:/etc/kubernetes/cert/
  8   done


2.4 創建加密配置文件

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# cat > encryption-config.yaml <<EOF
  4 kind: EncryptionConfig
  5 apiVersion: v1
  6 resources:
  7   - resources:
  8       - secrets
  9     providers:
 10       - aescbc:
 11           keys:
 12             - name: key1
 13               secret: ${ENCRYPTION_KEY}
 14       - identity: {}
 15 EOF


2.5 分發加密配置文件

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     scp encryption-config.yaml root@${master_ip}:/etc/kubernetes/
  7   done


2.6 創建審計策略文件

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# cat > audit-policy.yaml <<EOF
  4 apiVersion: audit.k8s.io/v1beta1
  5 kind: Policy
  6 rules:
  7   # The following requests were manually identified as high-volume and low-risk, so drop them.
  8   - level: None
  9     resources:
 10       - group: ""
 11         resources:
 12           - endpoints
 13           - services
 14           - services/status
 15     users:
 16       - 'system:kube-proxy'
 17     verbs:
 18       - watch
 19 
 20   - level: None
 21     resources:
 22       - group: ""
 23         resources:
 24           - nodes
 25           - nodes/status
 26     userGroups:
 27       - 'system:nodes'
 28     verbs:
 29       - get
 30 
 31   - level: None
 32     namespaces:
 33       - kube-system
 34     resources:
 35       - group: ""
 36         resources:
 37           - endpoints
 38     users:
 39       - 'system:kube-controller-manager'
 40       - 'system:kube-scheduler'
 41       - 'system:serviceaccount:kube-system:endpoint-controller'
 42     verbs:
 43       - get
 44       - update
 45 
 46   - level: None
 47     resources:
 48       - group: ""
 49         resources:
 50           - namespaces
 51           - namespaces/status
 52           - namespaces/finalize
 53     users:
 54       - 'system:apiserver'
 55     verbs:
 56       - get
 57 
 58   # Don't log HPA fetching metrics.
 59   - level: None
 60     resources:
 61       - group: metrics.k8s.io
 62     users:
 63       - 'system:kube-controller-manager'
 64     verbs:
 65       - get
 66       - list
 67 
 68   # Don't log these read-only URLs.
 69   - level: None
 70     nonResourceURLs:
 71       - '/healthz*'
 72       - /version
 73       - '/swagger*'
 74 
 75   # Don't log events requests.
 76   - level: None
 77     resources:
 78       - group: ""
 79         resources:
 80           - events
 81 
 82   # node and pod status calls from nodes are high-volume and can be large, don't log responses for expected updates from nodes
 83   - level: Request
 84     omitStages:
 85       - RequestReceived
 86     resources:
 87       - group: ""
 88         resources:
 89           - nodes/status
 90           - pods/status
 91     users:
 92       - kubelet
 93       - 'system:node-problem-detector'
 94       - 'system:serviceaccount:kube-system:node-problem-detector'
 95     verbs:
 96       - update
 97       - patch
 98 
 99   - level: Request
100     omitStages:
101       - RequestReceived
102     resources:
103       - group: ""
104         resources:
105           - nodes/status
106           - pods/status
107     userGroups:
108       - 'system:nodes'
109     verbs:
110       - update
111       - patch
112 
113   # deletecollection calls can be large, don't log responses for expected namespace deletions
114   - level: Request
115     omitStages:
116       - RequestReceived
117     users:
118       - 'system:serviceaccount:kube-system:namespace-controller'
119     verbs:
120       - deletecollection
121 
122   # Secrets, ConfigMaps, and TokenReviews can contain sensitive & binary data,
123   # so only log at the Metadata level.
124   - level: Metadata
125     omitStages:
126       - RequestReceived
127     resources:
128       - group: ""
129         resources:
130           - secrets
131           - configmaps
132       - group: authentication.k8s.io
133         resources:
134           - tokenreviews
135   # Get repsonses can be large; skip them.
136   - level: Request
137     omitStages:
138       - RequestReceived
139     resources:
140       - group: ""
141       - group: admissionregistration.k8s.io
142       - group: apiextensions.k8s.io
143       - group: apiregistration.k8s.io
144       - group: apps
145       - group: authentication.k8s.io
146       - group: authorization.k8s.io
147       - group: autoscaling
148       - group: batch
149       - group: certificates.k8s.io
150       - group: extensions
151       - group: metrics.k8s.io
152       - group: networking.k8s.io
153       - group: policy
154       - group: rbac.authorization.k8s.io
155       - group: scheduling.k8s.io
156       - group: settings.k8s.io
157       - group: storage.k8s.io
158     verbs:
159       - get
160       - list
161       - watch
162 
163   # Default level for known APIs
164   - level: RequestResponse
165     omitStages:
166       - RequestReceived
167     resources:
168       - group: ""
169       - group: admissionregistration.k8s.io
170       - group: apiextensions.k8s.io
171       - group: apiregistration.k8s.io
172       - group: apps
173       - group: authentication.k8s.io
174       - group: authorization.k8s.io
175       - group: autoscaling
176       - group: batch
177       - group: certificates.k8s.io
178       - group: extensions
179       - group: metrics.k8s.io
180       - group: networking.k8s.io
181       - group: policy
182       - group: rbac.authorization.k8s.io
183       - group: scheduling.k8s.io
184       - group: settings.k8s.io
185       - group: storage.k8s.io
186 
187   # Default level for all other requests.
188   - level: Metadata
189     omitStages:
190       - RequestReceived
191 EOF


2.7 分發策略文件

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     scp audit-policy.yaml root@${master_ip}:/etc/kubernetes/audit-policy.yaml
  7   done


2.8 創建訪問 metrics-server的證書和密鑰

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# cat > proxy-client-csr.json <<EOF
  3 {
  4   "CN": "aggregator",
  5   "hosts": [],
  6   "key": {
  7     "algo": "rsa",
  8     "size": 2048
  9   },
 10   "names": [
 11     {
 12       "C": "CN",
 13       "ST": "Shanghai",
 14       "L": "Shanghai",
 15       "O": "k8s",
 16       "OU": "System"
 17     }
 18   ]
 19 }
 20 EOF
 21 #創建metrics-server的CA證書請求文件



解釋:

CN 名稱需要位於 kube-apiserver 的 –requestheader-allowed-names 參數中,否則後續訪問 metrics 時會提示權限不足。

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
  3 -ca-key=/opt/k8s/work/ca-key.pem -config=/opt/k8s/work/ca-config.json \
  4 -profile=kubernetes proxy-client-csr.json | cfssljson -bare proxy-client	#生成CA密鑰(ca-key.pem)和證書(ca.pem)


2.9 分發證書和私鑰

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
  4   do
  5     echo ">>> ${master_ip}"
  6     scp proxy-client*.pem root@${master_ip}:/etc/kubernetes/cert/
  7   done


2.10 創建kube-apiserver的systemd

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# cat > kube-apiserver.service.template <<EOF
  4 [Unit]
  5 Description=Kubernetes API Server
  6 Documentation=https://github.com/GoogleCloudPlatform/kubernetes
  7 After=network.target
  8 
  9 [Service]
 10 WorkingDirectory=${K8S_DIR}/kube-apiserver
 11 ExecStart=/opt/k8s/bin/kube-apiserver \\
 12   --advertise-address=##MASTER_IP## \\
 13   --default-not-ready-toleration-seconds=360 \\
 14   --default-unreachable-toleration-seconds=360 \\
 15   --feature-gates=DynamicAuditing=true \\
 16   --max-mutating-requests-inflight=2000 \\
 17   --max-requests-inflight=4000 \\
 18   --default-watch-cache-size=200 \\
 19   --delete-collection-workers=2 \\
 20   --encryption-provider-config=/etc/kubernetes/encryption-config.yaml \\
 21   --etcd-cafile=/etc/kubernetes/cert/ca.pem \\
 22   --etcd-certfile=/etc/kubernetes/cert/kubernetes.pem \\
 23   --etcd-keyfile=/etc/kubernetes/cert/kubernetes-key.pem \\
 24   --etcd-servers=${ETCD_ENDPOINTS} \\
 25   --bind-address=##MASTER_IP## \\
 26   --secure-port=6443 \\
 27   --tls-cert-file=/etc/kubernetes/cert/kubernetes.pem \\
 28   --tls-private-key-file=/etc/kubernetes/cert/kubernetes-key.pem \\
 29   --insecure-port=0 \\
 30   --audit-dynamic-configuration \\
 31   --audit-log-maxage=15 \\
 32   --audit-log-maxbackup=3 \\
 33   --audit-log-maxsize=100 \\
 34   --audit-log-mode=batch \\
 35   --audit-log-truncate-enabled \\
 36   --audit-log-batch-buffer-size=20000 \\
 37   --audit-log-batch-max-size=2 \\
 38   --audit-log-path=${K8S_DIR}/kube-apiserver/audit.log \\
 39   --audit-policy-file=/etc/kubernetes/audit-policy.yaml \\
 40   --profiling \\
 41   --anonymous-auth=false \\
 42   --client-ca-file=/etc/kubernetes/cert/ca.pem \\
 43   --enable-bootstrap-token-auth \\
 44   --requestheader-allowed-names="aggregator" \\
 45   --requestheader-client-ca-file=/etc/kubernetes/cert/ca.pem \\
 46   --requestheader-extra-headers-prefix="X-Remote-Extra-" \\
 47   --requestheader-group-headers=X-Remote-Group \\
 48   --requestheader-username-headers=X-Remote-User \\
 49   --service-account-key-file=/etc/kubernetes/cert/ca.pem \\
 50   --authorization-mode=Node,RBAC \\
 51   --runtime-config=api/all=true \\
 52   --enable-admission-plugins=NodeRestriction \\
 53   --allow-privileged=true \\
 54   --apiserver-count=3 \\
 55   --event-ttl=168h \\
 56   --kubelet-certificate-authority=/etc/kubernetes/cert/ca.pem \\
 57   --kubelet-client-certificate=/etc/kubernetes/cert/kubernetes.pem \\
 58   --kubelet-client-key=/etc/kubernetes/cert/kubernetes-key.pem \\
 59   --kubelet-https=true \\
 60   --kubelet-timeout=10s \\
 61   --proxy-client-cert-file=/etc/kubernetes/cert/proxy-client.pem \\
 62   --proxy-client-key-file=/etc/kubernetes/cert/proxy-client-key.pem \\
 63   --service-cluster-ip-range=${SERVICE_CIDR} \\
 64   --service-node-port-range=${NODE_PORT_RANGE} \\
 65   --logtostderr=true \\
 66   --v=2
 67 Restart=on-failure
 68 RestartSec=10
 69 Type=notify
 70 LimitNOFILE=65536
 71 
 72 [Install]
 73 WantedBy=multi-user.target
 74 EOF



解釋:

  • –advertise-address:apiserver 對外通告的 IP(kubernetes 服務後端節點 IP);
  • –default-*-toleration-seconds:設置節點異常相關的閾值;
  • –max-*-requests-inflight:請求相關的最大閾值;
  • –etcd-*:訪問 etcd 的證書和 etcd 服務器地址;
  • –experimental-encryption-provider-config:指定用於加密 etcd 中 secret 的配置;
  • –bind-address: https 監聽的 IP,不能為 127.0.0.1,否則外界不能訪問它的安全端口 6443;
  • –secret-port:https 監聽端口;
  • –insecure-port=0:關閉監聽 http 非安全端口(8080);
  • –tls-*-file:指定 apiserver 使用的證書、私鑰和 CA 文件;
  • –audit-*:配置審計策略和審計日誌文件相關的參數;
  • –client-ca-file:驗證 client (kue-controller-manager、kube-scheduler、kubelet、kube-proxy 等)請求所帶的證書;
  • –enable-bootstrap-token-auth:啟用 kubelet bootstrap 的 token 認證;
  • –requestheader-*:kube-apiserver 的 aggregator layer 相關的配置參數,proxy-client & HPA 需要使用;
  • –requestheader-client-ca-file:用於簽名 –proxy-client-cert-file 和 –proxy-client-key-file 指定的證書;在啟用了 metric aggregator 時使用;
  • –requestheader-allowed-names:不能為空,值為逗號分割的 –proxy-client-cert-file 證書的 CN 名稱,這裏設置為 “aggregator”;
  • –service-account-key-file:簽名 ServiceAccount Token 的公鑰文件,kube-controller-manager 的 –service-account-private-key-file 指定私鑰文件,兩者配對使用;
  • –runtime-config=api/all=true: 啟用所有版本的 APIs,如 autoscaling/v2alpha1;
  • –authorization-mode=Node,RBAC、–anonymous-auth=false: 開啟 Node 和 RBAC 授權模式,拒絕未授權的請求;
  • –enable-admission-plugins:啟用一些默認關閉的 plugins;
  • –allow-privileged:運行執行 privileged 權限的容器;
  • –apiserver-count=3:指定 apiserver 實例的數量;
  • –event-ttl:指定 events 的保存時間;
  • –kubelet-*:如果指定,則使用 https 訪問 kubelet APIs;需要為證書對應的用戶(上面 kubernetes*.pem 證書的用戶為 kubernetes) 用戶定義 RBAC 規則,否則訪問 kubelet API 時提示未授權;
  • –proxy-client-*:apiserver 訪問 metrics-server 使用的證書;
  • –service-cluster-ip-range: 指定 Service Cluster IP 地址段;
  • –service-node-port-range: 指定 NodePort 的端口範圍。


提示:如果 kube-apiserver 機器沒有運行 kube-proxy,則還需要添加 –enable-aggregator-routing=true 參數。

注意:requestheader-client-ca-file 指定的 CA 證書,必須具有 client auth and server auth;

如果 –requestheader-allowed-names 為空,或者 –proxy-client-cert-file 證書的 CN 名稱不在 allowed-names 中,則後續查看 node 或 pods 的 metrics 失敗,提示:

  1 [root@zhangjun-k8s01 1.8+]# kubectl top nodes
  2 Error from server (Forbidden): nodes.metrics.k8s.io is forbidden: User "aggregator" cannot list resource "nodes" in API group "metrics.k8s.io" at the cluster scope
  3 


2.11 分發systemd

  1 [root@k8smaster01 ~]# cd /opt/k8s/work
  2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
  3 [root@k8smaster01 work]# for (( i=0; i < 3; i++ ))
  4   do
  5     sed -e "s/##MASTER_NAME##/${MASTER_NAMES[i]}/" -e "s/##MASTER_IP##/${MASTER_IPS[i]}/" kube-apiserver.service.template > kube-apiserver-${MASTER_IPS[i]}.service
  6   done
  7 [root@k8smaster01 work]# ls kube-apiserver*.service			#替換相應的IP
  8 [root@k8smaster01 ~]# cd /opt/k8s/work
  9 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
 10 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
 11   do
 12     echo ">>> ${master_ip}"
 13     scp kube-apiserver-${master_ip}.service root@${master_ip}:/etc/systemd/system/kube-apiserver.service
 14   done						                #分發systemd


三 啟動並驗證

3.1 啟動kube-apiserver服務

  1 [root@k8smaster01 ~]# source /opt/k8s/bin/environment.sh
  2 [root@k8smaster01 ~]# for master_ip in ${MASTER_IPS[@]}
  3   do
  4     echo ">>> ${master_ip}"
  5     ssh root@${master_ip} "mkdir -p ${K8S_DIR}/kube-apiserver"
  6     ssh root@${master_ip} "systemctl daemon-reload && systemctl enable kube-apiserver && systemctl restart kube-apiserver"
  7   done


3.2 檢查kube-apiserver服務

  1 [root@k8smaster01 ~]# source /opt/k8s/bin/environment.sh
  2 [root@k8smaster01 ~]# for master_ip in ${MASTER_IPS[@]}
  3   do
  4     echo ">>> ${master_ip}"
  5     ssh root@${master_ip} "systemctl status kube-apiserver |grep 'Active:'"
  6   done


3.3 查看kube-apiserver寫入etcd的數據

  1 [root@k8smaster01 ~]# source /opt/k8s/bin/environment.sh
  2 [root@k8smaster01 ~]# ETCDCTL_API=3 etcdctl \
  3     --endpoints=${ETCD_ENDPOINTS} \
  4     --cacert=/opt/k8s/work/ca.pem \
  5     --cert=/opt/k8s/work/etcd.pem \
  6     --key=/opt/k8s/work/etcd-key.pem \
  7     get /registry/ --prefix --keys-only


3.4 檢查集群信息

  1 [root@k8smaster01 ~]# kubectl cluster-info
  2 [root@k8smaster01 ~]# kubectl get all --all-namespaces
  3 [root@k8smaster01 ~]# kubectl get componentstatuses
  4 [root@k8smaster01 ~]# sudo netstat -lnpt|grep kube		#檢查 kube-apiserver 監聽的端口





提示:

如果執行 kubectl 命令式時輸出如下錯誤信息,則說明使用的 ~/.kube/config 文件不對,請切換到正確的賬戶后再執行該命令:

The connection to the server localhost:8080 was refused – did you specify the right host or port?

執行 kubectl get componentstatuses 命令時,apiserver 默認向 127.0.0.1 發送請求。當 controller-manager、scheduler 以集群模式運行時,有可能和 kube-apiserver 不在一台機器上,這時 controller-manager 或 scheduler 的狀態為 Unhealthy,但實際上它們工作正常;

6443: 接收 https 請求的安全端口,對所有請求做認證和授權;

由於關閉了非安全端口,故沒有監聽 8080。

3.5 授權


授予 kube-apiserver 訪問 kubelet API 的權限。

在執行 kubectl exec、run、logs 等命令時,apiserver 會將請求轉發到 kubelet 的 https 端口。本實驗定義 RBAC 規則,授權 apiserver 使用的證書(kubernetes.pem)用戶名(CN:kuberntes)訪問 kubelet API 的權限:

  1 [root@k8smaster01 ~]# kubectl create clusterrolebinding kube-apiserver:kubelet-apis --clusterrole=system:kubelet-api-admin --user kubernetes

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

雀巢砸逾620億 推動再生塑料使用

摘錄自2020年01月17日中央通訊社瑞士報導

瑞士食品大廠雀巢集團(Nestle)今天(17日)表示,將在5年內投資20億瑞士法郎(約新台幣621億元),以減少使用原始塑料,轉而採用食品級再生塑料。

法新社報導,雀巢也計劃投資新型永續包裝,以達到該公司的目標,也就是在2025年前全面使用可回收或可再利用的包裝材料。雀巢旗下品牌包含Nespresso咖啡、Vittel礦泉水和Smarties巧克力。

此外,雀巢發表聲明說,他們會在未來5年內,將原始塑料使用量降低1/3,且會撥出2億5000萬瑞士法郎作為創投基金,投資致力於廢棄物回收事業的新創公司。為打造再生塑料市場,雀巢計劃採購重達200萬公噸的食品級再生塑料,並撥出超過15億瑞士法郎,以支付從現在到2025年所需的材料附加費。雀巢執行長施奈德(Mark Schneider)在聲明中說:「最終不應有塑膠被丟入垃圾掩埋場,或成為廢棄物。」

綠色和平組織(Greenpeace)瑞士分部的維特里希(Matthias Wuthrich)表示,雀巢這項聲明「僅部分令人振奮」。他說:「這是朝正確方向所跨出的一步,但要結束當前危機,必須終止無用的塑膠生產作業,並須採用新的供應系統。」

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※評比南投搬家公司費用收費行情懶人包大公開

升溫也列車禍原因! 研究:美國事故死亡人數隨暖化提高 且以年輕人、男性居多

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

聯合國裁決 遣返氣候難民恐違人權義務

摘錄自2020年1月21日中央社報導

聯合國人權事務委員會今天(21日)表示,政府若遣返難民,把他們送回深受氣候變遷影響的母國,可能違反人權義務。

法新社報導,聯合國人權事務委員會(UN Human Rights Committee)的獨立專家發布一份不具約束力、但受到密切關注的裁決。這項裁決的起因是太平洋島國吉里巴斯人泰提奧塔(Ioane Teitiota)提出的一起異議。

泰提奧塔表示,他出身的南塔拉瓦島(South Tarawa)已經人滿為患,造成土地糾紛、安全飲水來源減少,因為海平面上升使得吉里巴斯其他島嶼已不宜人居。

法新社提到,國際特赦組織(Amnesty International)太平洋研究員舒茲(Kate Schuetze)聲明說,這項裁決創下全球先例,「傳遞的訊息很清楚,太平洋島國不必等到被海水淹沒,才觸發保障生存權的人權義務」。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※評比南投搬家公司費用收費行情懶人包大公開

墨西哥帝王蝶保育專家人間蒸發 恐遭盜伐集團毒手

摘錄自2020年1月21日自由時報報導

墨西哥米卻肯州(Michoacan state)21日傳出,在當地設立帝王斑蝶保育園區羅薩利歐(El Rosario)的蝴蝶保育專家戈麥茲(Homero Gomez)已失蹤超過1週,外界擔憂他已經慘遭犯罪集團毒手。

據 ,戈麥茲本月13日早晨發訊息推廣蝴蝶保育後,手機即失去訊號,家人通報失蹤至今已超過1週,外界擔憂他恐怕已慘遭犯罪集團綁架或毒手。目前已有超過200名志工開始協尋戈麥茲,人權團體也請求官方加強搜索戈麥茲的下落,同時調查他的失蹤是否與盜伐集團有關聯。墨西哥自2006年起已有超過6萬人失蹤,其中不乏人權工作者及環境保育者。

截圖自戈麥茲

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

AI的真實感

目錄

​ AI的真實感一直是遊戲AI程序員追求的目標,如何做出能給玩家真實感,挑戰性又不會勸退玩家的AI,既需要AI程序員有一定的程序功底,還需要廣泛地閱讀和遊戲人工智能相關的課題,比如:心理學,生物學,認知科學乃至軍事戰術等,在你閱讀時你會不斷迸發出更好的想法;還需要和團隊多溝通,無論是程序員還是策劃,甚至是老闆,他們有時一個好的想法就能為你設計的遊戲AI增強真實性。

請牢記一點:遊戲AI的真實感需要服務於遊戲本身,其唯一目的是讓玩家玩遊戲覺得更有趣。

雖然本篇博客會較少代碼講解,但更多地是希望講解一些學習AI過程中遇到的或想到的方法,然後可以根據做的遊戲的不同將這些想法融入設計的遊戲AI里。

1.讓AI”不完美”–估算和假設

​ 遊戲AI並非越智能越好,因為就算是人類,有時也會犯錯,所以為了讓遊戲AI更真實,有時需要讓遊戲AI犯錯,才會使玩家有更愉快的遊戲體驗(而不是被電腦打爆)。

有兩種方式誘使AI犯錯:

  1. 先使AI“完美”,再讓它變傻

  2. 當設計AI使用的算法時,通過假設和估算,允許悄悄混進“錯誤”。

​ 以足球AI為例,前者用一個取值為一個固定大小範圍的隨機值的變量作為干擾值(隨機噪聲),使AI每次判斷踢球方向時產生小錯誤;後者是讓AI用圓而非橢圓(估算)來描述對手的截球範圍,既簡化了AI算法,又實現”不完美“。當然,在不同的遊戲里如何使AI犯錯就得因遊戲而論了。

​ 而在不少FPS遊戲里,在遊戲AI第一次射擊玩家時讓其專門射偏是一個好主意,能提醒玩家有AI的出現,在未受傷前做好準備。尤其在玩家需要探索一間充滿敵人的屋子時,給了玩家一個了解基本形勢的機會並量力而行,不至於一進門就被集火殺死。

​ 射偏還有其他好處:

  1. 比如射偏的是飛行軌跡明顯或者會留下痕迹的箭,火箭等,讓它故意射偏在玩家的視野範圍之內會大大提高遊戲的刺激感
  2. 當玩家瀕臨死亡時,故意讓AI射擊準確性降低,會讓玩家有一個體驗困境中躲過槍林彈雨,最後堅強獲勝的驚心動魄的機會(像美國槍戰大片那樣),大大提高遊戲的有趣性。

2 AI感知

​ 對於許多類遊戲而言,想讓AI看起來智能,需要AI有精確的感知模仿,並不是說簡單地模仿立體視覺和聽力,還要其決策邏輯和其感知能力保持一致。

有些遊戲不太需要這種感知模擬,一是它會佔用不少的CPU和內存的資源,二是有些遊戲使用這種感知模擬並不會提升遊戲體驗。

全能感知

1.你安靜地接近一個敵人AI打算背後刺殺,結果他立刻轉過身一槍把你秒了(可能聽到眨眼睛的聲音)。

【我起了,一槍秒了,有什麼好說的】

2.你潛行在黑暗處匍匐前行,旁邊一個守衛明明看不到暗處,就突然發現了你,給你來了一槍。

【是不是玩不起?AI開掛實錘】

​ 這就是典型的具有全能的感知能力的AI,這可能是因為AI程序員為了輕鬆或沒有考慮到事實和感知,這樣的AI只會讓玩家失去玩遊戲的興趣。因此需要將AI的視覺和聽覺限製得與玩家一樣,聽力範圍限制,無法看到黑暗裡的物品,視力範圍90°,不能透視等。

​ 這又牽扯出一個經典的話題:AI可以作弊嗎?

​ 這裏的作弊不僅指的是AI使用了玩家不能使用的能力,或者單方面獲取比玩家更多的信息,獲得更多資源,還包括AI擁有程序員設計的完善的決策設計,當他能夠打敗玩家時卻放棄了那個可以獲勝的決策而製造出失誤的表象,也就是放水

​ 我認為是可以的。其實大部分遊戲的AI都是會作弊的,除非沒必要,比如紅警,街霸,還有不少戰略遊戲裏面,AI都是會作弊的,因為這樣能更快速有效地給玩家製造緊迫感,實現難度控制。 需要給予作弊能力的關鍵在於玩家和遊戲AI之間天生處於不平等地位

​ 大部分AI還未能實現自我學習的能力,都是靠經驗豐富的遊戲AI程序員花費大量時間去模擬玩家實現接近玩家的AI,而且目前費了好大勁實現的自主學習的AI又其實與會作弊的AI(指的是作弊得恰到好處的)給予玩家的遊戲體驗的差別並不大,廠商為了遊戲體驗和成本,街機廳的老闆為了恰飯,所以才會選擇了給予作弊。

​ 在《可汗:戰爭之王2》(一款即時戰略遊戲)中,有兩個巧妙的作弊:

  1. 在探索地圖時,每30秒左右給AI一個隨機的機會作弊,如果有機會作弊,就讓AI去探索有些好東西的區域,這可以幫助我們避免AI不幸碰巧沒找到任何附近的資源的狀況發生。
  2. 在一個區域內保持和對手的實力相近,這使得我們能夠分配合理的兵力去進攻和防守,以避免兵力部署的徒勞無功。

​ 歸根結底,當且僅當AI作弊能提高玩家體驗時,就應該讓AI作弊。但記得一點:讓你的AI作弊得不易被發現,否則就是另一種狀況了。

圖為《可汗:戰爭之王2》

特定感覺無知

​ 1.你和精英怪單挑,一套下去發現打不過,瀕臨死亡被逼到角落陰暗處,精英怪丟失了目標不再攻擊,回到原本看守的地方,你莫名其妙活下來了。

​ 2.你刺殺了一位守衛,他臨死前叫了一聲,一群守衛立刻趕來,你來不及隱藏屍體就躲進衣櫃,緊張地準備着守衛發現屍體后搜索房間時的一場惡戰,結果他們從屍體上踩了過去,彷彿沒看到屍體。

​ 這些情況是因為AI感知得太少,也說明了為了讓AI更真實,需要一種機制讓AI模仿人類的短期記憶。

​ 在我博客所參考的那本書里舉了個很生動的例子:

​ 當一名AI遇到兩名玩家,他的視野里有兩名與他距離不同的分別位於左右的玩家,兩條線表示視野

​ 當他判斷左邊那個玩家離他更近,對他威脅程度更大時,他往左邊轉準備攻擊左邊玩家,這時他一左轉右邊玩家丟失在他視野之外(AI也就不會再警惕右邊玩家)

​ 然後那名玩家(丟失在右邊視野的)直接走到AI右邊將其殺死

​ 這個例子就很形象說明了AI需要短期記憶,不然若遊戲裏面發生上面所舉這個例子,玩家會強烈地感覺到: 我上當了!他不是人!

​ 至於如何實現短期記憶,視你所期望的遊戲效果而自由改變,一般是以一個值作為可記憶時間,用最後敵人出現的位置作為記錄,增加相應的決策判斷,並在下次發現該對象時更新記憶系統相關信息,當超過記憶時間或決策判斷不會對自身產生影響則將其移除於記憶系統。

3 AI的個性

​ 不同的AI如果有不同的角色設計,就需要在其AI行為方面表現其個性,就算沒有角色設計,也應該盡量避免所有的AI的思考方式是一致的(容易讓玩家感受到虛假感),以產生有隨機個性的AI。

​ 簡單來說,因為AI的行為決策系統計算不同行為的期望值分數都被限制在同一範圍內(如 0~1),那我們可以通過對每個分數乘以其所需個性趨向的一個偏移常量,就能輕鬆生成有不同個性特點的AI。比如FPS遊戲里,膽小的人永遠會把自己的生存放在第一位而較為謹慎,所以將其對藥品和防具的尋找行為的期望值偏量設為1.5,而暴躁好鬥的人會更趨向於進攻,所以將其攻擊目標的行為期望值偏量設為1.5。

​ 如果你的遊戲設計要求一個角色的個性在該遊戲中保持不變,那你就需要為每個具有個性的角色建立單獨的腳本文件,用來存放其所有特性數據。如果你的遊戲設計沒有什麼角色個性設計,那麼你也可以在決策系統中計算期望值分數時加入一個固定大小範圍的隨機個性偏移量,以生成不同行為趨向的角色。

​ 而除了隨機個性偏移量,有些時候還會由角色設計而增加更多個性化的選擇趨向,如武器方面,說話方式,甚至塑造人物精神,增加犧牲自我,保護隊友的決策。

​ 在使命召喚中我印象最深的是格里戈斯上士 ,臨危之際不忘把隊友”肥皂” 拖向掩體!然後自己卻與敵人繼續交火,獻出了自己的生命,當時玩的時候感覺非常的感動,確定是塑造了一位有血有肉的有偉大精神的真實角色形象。

圖右一為格里戈斯上士

4 AI的預判

​ 首先是一些遊戲不太需要AI具有預判性,比如:說galgame,劇情解密類;而一些遊戲就需要具有預判性的AI,比如說:策略類遊戲,棋類遊戲。具有一定的預判性的AI會給玩家帶來驚喜,真實感與挑戰性(不要盲目增強,如果整個阿爾法狗出來就直接勸退玩家了)。

​ 簡單的預判比如說:

​ 當地圖上的某些特定的物件即將重生時,比如說LOL裏面的野區資源,又或者說是守望先鋒裏面固定點重新刷新的醫療包,出色的玩家會提前準備好前往目標處以保證搶先佔有它,那就可以讓遊戲AI用算法提前預計最近的即將再生或未被奪取的資源點,並規劃路徑前往。

圖為lol里蓋倫預判敵人路過補視野草叢埋伏

​ 又或者是在攻擊和追逐敵人時計算移動目標位置時加入敵人的速度及運動方向,預判其下個時間的位置進行追逐或攻擊。

​ 複雜的預判比如說:

​ 在FPS遊戲中,出色的老兵在打傷了敵人後,會提前到距離受傷敵人最近的醫療包處進行埋伏,當敵人到來時給予致命一擊。那麼遊戲AI也可以模仿這種預判思維,在攻擊成功判斷後添加一個搜索並前往離受傷者最後出現的地方最近的醫療包處進行埋伏的決策,可以想象到玩家如果遇到這種會預判的電腦時的驚訝,並激起其挑戰慾望。

​ 當然,預判總會有失策的時候,人類有時不也是如此,比如有些玩家不去找醫療包,或者摸清AI的習慣(只會埋伏最近那個醫療點)等情況,那麼就提醒你,不能所有AI都是具有同樣的預判思想(可以通過AI個性來生成不同思考方式的AI),甚至就如一個玩笑:我預判到你預判到我會預判,於是我不預判了。沒有預判的AI也許就是具有最強預判性的AI呢?滑稽.jpg

5 AI的智能等級

​ 正如一本書中寫道:老謀深算的AI應該跟它的壽命成正比。倘若設計一個採用了前沿技術,具有複雜思考系統的AI,但如果它在遊戲中僅僅被期望活幾秒鐘的話,那將毫無意義。

​ 而且AI的智能程度也需要與其設計的角色的智能等級相匹配。比如說一個戰爭策略遊戲里,不同的士兵有不同的軍銜,比如 上尉、中尉、少尉 等,越高軍銜的軍官一般會有越高的智能等級,所以需要相應地提高其智能程度,比如加入較為複雜的戰略決策。

​ 而拿簡單的舉例:假如你在遊戲里潛行時不小心發出聲響,那麼普通守衛只是過來看了看周圍,沒發現什麼人就又回到原來的崗位,而精英守衛就可以加入更為精明或謹慎的行為:先往發出聲響的地方(如草場)掃射一下,若沒發現異常才將狀態改為正常守衛,返回其守衛處,或者加入殺個回馬槍的決策。這些有趣的謹慎行為也會讓玩家更不敢把AI當成是傻瓜,甚至感受到樂趣。不過還是記住設計AI的原則,不要讓AI過於謹慎導致玩家喪失樂趣。

是否需要絕對的真實?

先來看看兩個例子

  1. 如果AI足夠聰明,當他遭遇到過一次玩家並進行對戰後便能意識到自己沒有機會戰勝玩家,此時選擇一個隱蔽的地方(如房門后)進行埋伏玩家死活不肯主動出擊,這樣的做法無疑是最明智的。
  2. 如果玩家控制的角色在遊戲中受到的傷害達到一定程度,玩家一定會讓角色逃離戰場或者試圖進行恢復,但如果AI這麼做,那麼玩家就不得不進行追殺,玩家殺死他就十分困難。

這兩種結果就使得遊戲要麼像一個埋伏式(持久戰)的較量,要麼像比賽追逐的遊戲,兩種情況無疑都比較無聊。所以絕對的真實也可能帶來遊戲有趣性的下降。

​ 雖然追求AI能給人帶來真實感,但玩家其實也有一定的寬容能力,具有瑕疵的AI他們也會自行腦補:畢竟它終究是個AI。所以不必過分追求真實,又不能輕視AI真實感對於遊戲體驗的提升的重要性,還是牢記最重要的一點,也是追求AI的真實感的唯一目的:讓遊戲更有趣!

參考學習自《遊戲人工智能編程案例精粹》《遊戲人工智能》

轉載標明出處:作者AMzz 博客:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※評比南投搬家公司費用收費行情懶人包大公開

多線程之美1一volatile

目錄
一、java內存模型
1.1、抽象結構圖
1.2、概念介紹
二、volatile詳解
2.1、概念
2.2、保證內存可見性
2.3、不保證原子性
2.4、有序性

一、java內存模型

1.1、抽象結構圖

1.2、概念介紹

  • java 內存模型

    即Java memory model(簡稱JMM), java線程之間的通信由JMM控制,決定一個線程對共享變量的寫入何時對另一個線程可見。
  • 多線程通信通常分為2類:共享內存和消息傳遞

     JMM採用的就是共享內存來實現線程間的通信,且通信是隱式的,對程序開發人員是透明的,所以在了解其原理了,才會對線程之間通信,同步,內存可見性問題有進一步認識,避免開發中出錯。
  • 線程之間如何通信?

    在java中多個線程之間要想通信,如上圖所示,每個線程在需要操作某個共享變量時,會將該主內存中這個共享變量拷貝一份副本存在在自己的本地內存(也叫工作內存,這裏只是JMM的一個抽象概念,即將其籠統看做一片內存區域,用於每個線程存放變量,實際涉及到緩存,寄存器和其他硬件),線程操作這個副本,比如 int i = 1;一個線程想要進行 i++操作,會先將變量 i =1 的值先拷貝到自己本地內存操作,完成 i++,結果 i=2,此時主內存中的值還是1,在線程將結果刷新到主內存后,主內存值就更新為2,數據達到一致了。
    
    如果線程A,線程B同時將 主內存中 i =1拷貝副本到自己本地內存,線程A想要 將i+1,而線程B想要將 int j=i,將賦值給j,那麼如何保證線程之間的協作,此時就會涉及到線程之間的同步以及內存可見性問題了。(後文分析synchronized/lock)
     那線程之間實現通信需要經過2個步驟,藉助主內存為中間媒介:
       線程A (發送消息)-->(接收消息) 線程B  
       1、線程A將本地內存共享變量值刷新到主內存中,更新值;
       2、線程B從主內存中讀取已更新過的共享變量;
  • 共享內存中涉及到哪些變量稱為共享變量?

    這裏的共享內存指的是jvm中堆內存中,所有堆內存在線程之間共享,因為棧中存儲的是方法及其內部的局部變量,不在此涉及。
    共享變量:對於多線程之間能夠共同操作的變量,包含實例域,靜態域,數組元素。即有成員變量,靜態變量等等,
       不涉及到局部變量(所以局部變量不涉及到內存可見性問題)
  • 多線程在java內存模型中涉及到三個問題

    • 可見性
    • 原子性
    • 有序性(涉及指令重排序)

二、volatile詳解

2.1、概念

-1、volatile 是 java中的關鍵字,可修飾字段,可以保證共享變量的在內存的可見性,有序性,不保證原子性。
-2、作用:在了解java內存模型后,才能更加了解volatile在JMM中的作用,volatile在JMM中為了保證內存的可見性,即是線程之間操作共享變量的可見性。
  • volatile寫和讀的內存語義
volatile 寫的內存語義:
    當寫一個volatile修飾的共享變量時,JMM會把該線程的本地內存的共享變量副本值刷新到主內存中;
volatile 讀的內存語義:
    當讀一個volatile修飾的共享變量時,JMM會將該線程的本地內存的共享變量副本置為無效,要求線程重新去主內存中獲取最新的值。
  • java內存模型控制與volatile衝突嗎?什麼區別?
不衝突!java內存模型控制線程工作內存與主內存之間共享變量會同步,即線程從主內存中讀一份副本到工作內存,又刷新到主內存,那怎麼還需要 volatile來保證可見性,不是JMM自己能控制嗎,一般情況下JMM可以控制 2份內存數據一致性,但是在多線程併發環境下,雖然最終線程工作內存中的共享變量會同步到主內存,但這需要時間和觸發條件,線程之間同時操作共享變量協作時,就需要保證每次都能獲取到主內存的最新數據,保證看到的工作變量是最後一次修改后的值,這個JMM沒法控制保證,這就需要volatile或者後文要講的 synchronized和鎖的同步機制來實現了。

2.2、保證內存可見性

  • 1、多個線程出現內存不可見問題示例

    /**
     * @author zdd
     * Description: 測試線程之間,內存不可見問題
     */
    public class TestVisibilityMain {
        private static boolean isRunning = true;
    
      // 可嘗試添加volatile執行,其餘不變,查看線程A是否被停止
      //private static volatile boolean isRunning = true;
    
        public static void main(String[] args) throws InterruptedException {
    //1,開啟線程A,讀取共享變量值 isRunning,默認為true 
            new Thread(()->{
              // --> 此處用的lamda表達式,{}內相當於Thread的run方法內部需執行任務 
                System.out.println(Thread.currentThread().getName() + "進入run方法");
                while (isRunning == true) {
                }
                System.out.println(Thread.currentThread().getName()+"被停止!");
            },"A").start();
            //2,主線程休眠1s, 確保線程A先被調度執行
            TimeUnit.SECONDS.sleep(1);
        //3,主線程修改共享變量值 為flase,驗證線程A是否能夠獲取到最新值,跳出while循環  --> 驗證可見性
            isRunning =false;
            System.out.println(Thread.currentThread().getName() +"修改isRunning為: " + isRunning);
        }
    }

​ 執行結果如下圖:

  • 2、一個容易忽視的問題
 上面代碼 while裏面是一個空循環,沒有操作,如果我在裏面加一句打印語句,線程A會被停止,這是怎麼回事呢?
 原:while (isRunning == true) {}
 改1:
 while (isRunning == true) {
     System.out.println("進入循環");
 }
原來 println方法裏面加了 synchronized關鍵字,在加了鎖既保證原子性,也保證了可見性,會實現線程的工作內存與主內存共享變量的同步。
源代碼如下:
 public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }
  改2:
  while (isRunning == true) {
       //改為這樣,也可以停止線程A
                synchronized (TestVisibilityMain.class){}
   }

2.3、不保證原子性

  • 1、示例代碼
/**
 * @author zdd
 * Description: 測試volatile的不具有原子性
 */
public class TestVolatileAtomic {

    private static volatile   int  number;
    //開啟線程數
    private static final  int THREAD_COUNT =10;
    //執行 +1 操作
    public static void  increment() {
       //讓每個線程進行加1次數大一些,能夠更容易出現volatile對複合操作(i++)沒有原子性的錯誤
        for (int i = 0; i < 10000; i++) {
          number++;
        }
        System.out.println(Thread.currentThread().getName() +"的number值: "+number);
    }

    public static int getNumber() {
        return number;
    }

    public static void main(String[] args) throws InterruptedException {
        TestVolatileAtomic volatileAtomic  = new TestVolatileAtomic();
        Thread[] threads = new Thread[THREAD_COUNT];
        for (int i = 0; i < THREAD_COUNT; i++) {
            threads[i]=
            new Thread(()->{
               // 做循環自增操作
                volatileAtomic.increment();
                System.out.println(Thread.currentThread().getName() +"的number值: "+volatileAtomic.getNumber());
            },"thread-"+i);
        }

        for (int i = 0; i <10; i++) {
          //開啟線程
            threads[i].start();
        }
        //主線程休眠4s,確保上麵線程都執行完畢
        TimeUnit.SECONDS.sleep(4);
        System.out.println("執行完畢,number最終值為:"+volatileAtomic.getNumber());
    }
}

執行結果:number的最後值不一定是 10*10000= 100000的結果
  • 2、解決上訴問題
 
  //1,increment()方法上加上 synchronized關鍵字同步
    public static synchronized void  increment() {
       //讓每個線程進行加1次數大一些,能夠更容易出現volatile對複合操作(i++)沒有原子性的錯誤
        for (int i = 0; i < 10000; i++) {
          number++;
        }
         System.out.println(Thread.currentThread().getName() +"的number值: "+number);
    }
  //2,使用Lock,使用其實現類可重入鎖 ReentrantLock
    static Lock lock = new ReentrantLock();
    //執行 +1 操作
    public static   void  increment() {
        lock.lock();
        try {
            for (int i = 0; i < 10000; i++) {
                number++;
            }
            System.out.println(Thread.currentThread().getName() + "的number值: " + number);
        } finally {
            lock.unlock();
        }
    }

運行結果如圖:

  • 3、原因分析
對單個volatile變量的讀/寫具有原子性,而對像 i++這種複合操作不具有原子性。
上面代碼 i++操作可以分為3個步驟
-1 先讀取變量i的值   i
-2 進行i+1操作   temp= i+1
-3 修改i的值     i= temp
比如:比如在線程A,B同時去操作共享變量i, i的初始值為10,A,B同時去獲取i的值,A對i進行 temp =i+1,此時i的值還沒變, 線程B也對i進行 temp=i+1了,線程A執行i=temp的操作,i的值變為11,此時由於 volatile可見性,會刷新A的 i值到主內存,主內存中i此時也更新為11了,線程B接收到通知自己i無效了,重新讀取i=11,雖然i=11,但是已經進行過 temp= i+1了,此時temp =11,線程B繼續第三步,i=temp =11, 預期結果是i被A,B自增各一次,結果i=12,現在為11,出現數據錯誤。

2.4、有序性

  • 重排序
-1,重排序概念:重排序是編譯器和處理器為了優化程序性能而對指令序列重新排序的一種手段
即:程序員編寫的程序代碼的順序,在實際執行的時候是不一樣的,這其中編譯器和處理器在不影響最終執行結果的基礎上會做一些優化調整,有重新排序的操作,為了提高程序執行的併發性能。
-2,重排序分類: 編譯重排序,處理器重排序
-4,單線程下,重排序沒有問題,但是在多線程環境下,可能會破壞程序的語義.
  • volatile 防止重排序保證有序性

為了實現volatile的內存語義,JMM會限制編譯器和處理器重排序

-1 制定了重排序規則表防止編譯器重排序

volatile重排序規則表(圖摘自書-併發編程的藝術)

-2 插入內存屏障防止處理器重排序

參考資料:
1、Java併發編程的藝術- 方騰飛
2、java多線程編程核心技術- 高洪岩

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

對於計算機相關專業我們在迷茫什麼

計算機相關專業初識–對於計算機相關專業我們在迷茫什麼

前言

由於種種原因,迫使我寫下這篇博客,我相信,初入計算機相關專業的萌新肯定很迷茫,我該學什麼,我該如何去學,我該如何學好等等問題纏繞心頭。有很多學弟學妹問我該如何去學計算機相關專業,作為過來人,我決定將我的所知所得寫下來,讓初入計算機相關專業的萌新的學習之路走得更順暢一些。

一、什麼是計算機

對於剛學習計算機相關專業的萌新來說,了解一下計算機的工作原理是十分必要的,但是在這裏我們不過多闡述,讓大家簡單了解一下就好。

讓我們先來看一下對於計算機名詞的解釋:

計算機(computer)俗稱電腦,是現代一種用於高速計算的电子計算機器,可以進行數值計算,又可以進行邏輯計算,還具有存儲記憶功能。是能夠按照程序運行,自動、高速處理海量數據的現代化智能电子設備。

划重點:

  • 我們注意到,計算機就是一種用於進行數值計算的現代化智能电子設備。需要理解的是為什麼是進行數值計算,在這裏,你會疑惑,為什麼是數值計算呢,我輸入的明明不是数字呀?這個問題很容易解釋清楚,因為計算機只是一種电子設備,它不具有人類獨立思考和不斷學習的能力,它的所有功能都是事先設定好的,所以當計算機面對輸入字符的時候,會將它統一按照ASCII(計算機編碼系統)規則轉換為數值“0”和“1”(二進制數值),所以,在計算機里,數據存儲都是用“0”和“1”(即二進制數值)來實現。

  • 還有一點值得注意,按照程序運行,那麼問題來了,程序是什麼?程序就是一組計算機能識別和執行的指令, 它以某些程序設計語言編寫,運行於某種目標結構體繫上 。舉個例子,程序就像是用英語(程序設計語言,例如c,c++)寫的文章,要讓一個懂的英語的人(編譯器,如C的編譯器gcc,這裏要注意編譯器和IDE的區別,通常IDE包含編譯器)同時也會閱讀這篇文章的人(結構體系)來閱讀、理解、標記這篇文章。

有學妹問過我,為什麼簡單的代碼,能實現豐富的效果。其實這取決於編譯器的強大能力。下面來簡單介紹一下,編輯器,編譯器,IDE(集成開發環境)的區別。

  • 編輯器:編輯器就是用來編輯的軟件,比如windows自帶的記事本就是一個編輯器, 記事本沒有語法高亮,不显示行號,當一段可執行代碼寫完后無法通過內置環境執行,必須手動輸入命令執行編譯等等一些弊端,所以很少有程序員會用記事本去寫代碼 , 寫代碼比較好用的編輯器軟件有vscode,vim,sublime,notepad++,emacs,atom等等 ,雖然編輯器原始功能不足,但是開發人員為了使編輯器更加友好,所以有很多內置插件可供使用,完全可以手動打造一個IDE。
  • 編譯器:簡單來說,編譯器就是將“一種語言(一般為高級語言,如c,c++,java,python等,計算機不可直接識別和執行)”翻譯為“另一種語言(一般為低級語言,低級語言即機器語言,機器語言是用二進制代碼錶示的計算機能直接識別和執行的一種機器指令的集合)”的程序。舉個例子,用Dev-C++寫好一段可執行"hello,world!"C語言代碼之後,我們要讓它在屏幕打印出來我們想要它輸出的"hello,world!",就需要通過gcc編譯器執行編譯后才能显示。其他語言同理。
  • IDE:集成開發環境,用於程序開發環境的應用程序,一般包含代碼編輯器編譯器調試器圖像用戶界面等工具。集成了代碼編寫程序分析程序編譯程序調試等功能。如 jetbrains 的用於Java開發的 IntelliJ IDEA 、用於JavaScript開發的WebStorm、用於Python開發Pycharm,微軟的 Visual Studio系列 ,IBM的Eclipse。

二、我們該學什麼

很多初入計算機相關專業的萌新,總是很迷茫,不知道自己該學什麼,通常是他們知道如何去學好學校開設的每一門課程,就是不知道自己該向哪些方向學習,這些方向指的是專業技能和就業方向,諸如web開發、Android/IOS開發、數據分析、人工智能、網絡安全、遊戲開發、軟件測試等等。有這種疑惑很正常,迷茫也是正常的,但我們總要讓自己了解自己所需,然後腳踏實地,一步一步去充實自己的能力。而我想做的也很簡單,就是幫助大家解除心裏的疑惑。那麼,我們開始進入正題。

1. 我們該如何選擇適合自己的方向

對於這個問題,其實是很難回答清楚的,因為每個人的興趣都不相同,所以就很難去站在自己的角度去回答疑問者的問題。但是,原理都是想通的,我相信我的經驗會幫助到你們。

  • 通常,學校每學期都會開設一門或多門語言(程序設計語言,下文同),那麼,喜歡一門語言,首先要愛上它的語言風格,諸如Java的嚴謹,Python的自由,總有一款適合你;其次,在學習語言的過程中,一定要了解它能幹什麼,市場環境如何,工作崗位多少等綜合因素,再決定要不要去深入這門語言,並且主攻自己感興趣的那個方向。

  • 對於學校沒有開設,但是自己又想學習的語言而言,該如何去選擇。首先,學校開設的語言基本是市場比較流行的語言,也符合市場需求,所以,完全可以在學校開設的語言中去選擇自己想要了解並學習的語言。此外,我們可以藉助 TIOBE ( TIOBE 編程社區指數是編程語言流行度的指標,該榜單每月更新一次,指數基於全球技術工程師、課程和第三方供應商的數量。包括流行的搜索引擎,如谷歌、必應、雅虎、維基百科、亞馬遜、YouTube 和百度都用於指數計算。 )去了解語言的流行程度,流行程度決定市場需求,以此來參考自己想要了解並學習的語言,在此附上2019年11月語言排名。

2. 主流編程語言主要應用場景

  • Java

    1. 企業級應用開發: 大到全國聯網的系統,小到中小企業的應用解決方案,Java都佔有極為重要的地位 。
    2. web後端開發: JSP+Servlet+JavaBean 是一種比較流行的開發模式。
    3. 移動領域:手機遊戲。
    4. Android App開發: android 開發只用到了JAVA的語法和JAVA SE的一小部分API。
  • C

    C語言是一門基礎語言,是其他一些語言的基礎,例如MATLAB,Object-C,Lua等.同時也是學習來比較難的語言,達到精通的程度沒有3-10年左右很難,C語言沒有比較完善的開發框架,是面向過程的一門語言,講究算法跟邏輯。

    1. 科研
    2. 服務器: 網絡核心設備,如路由器、交換機、防火牆。
    3. 操作系統:類unix系統(Linux/freebsd)
    4. 嵌入式開發: 在一個特定的硬件環境上開發與構建特定的可編程軟件系統的綜合技術。
    5. 自動化控制
  • Python

    1. 圖形處理
    2. 數學處理
    3. 文本處理
    4. 數據庫編程
    5. 網絡編程
    6. 多媒體應用
    7. pymo引擎: 運行於Symbian S60V3,Symbian S60V5,Symbian 3,Android,Windows,Linux,Mac Os,Maemo,MeeGo系統上的AVG遊戲引擎。
    8. 黑客編程
    9. 網絡安全
  • C++

    1. 遊戲開發
    2. 科學計算
    3. 網絡軟件
    4. 操作系統
    5. 設備驅動程序
    6. 移動設備
    7. 嵌入式開發
    8. 科研
    9. 編譯器
  • C#

    1. web後端開發
    2. 桌面軟件開發
    3. 人工智能
    4. 遊戲開發
  • JavaScript
    唯一能用於前後端開發的語言web前端開發
    1. web前端開發
    2. node web後端開發
    3. 操作系統
    4. 後台
    5. 桌面軟件開發
    6. 混合App
    7. 小程序
  • PHP

    1. web後端開發
    2. 桌面軟件開發
    3. 命令行腳本
  • SQL

    1. 操作數據庫
  • Swift

    1. 蘋果生態系統應用開發
  • Ruby

    1. web開發
  • R

    數據科學闖天下,左手Python右手R

    1. 機器學習
    2. 數據分析
    3. 科學計算
  • Go

    1. web後端開發
    2. 高性能服務器應用

3. 主流編程語言學習路徑(將持續更新,僅供參考)

  • JavaScript

4. 主流編程語言入門學習書籍推薦

語言 書籍
C 《嗨翻C語言》
C++ 《C++權威教程》
Java 《Java輕鬆學》
Python 《Python編程從入門到實戰》
JavaScript 《JavaScript入門經典》
PHP 《PHP編程實戰》
SQL 《SQL基礎教程》
Swift 《Swift編程權威指南》
Ruby 《Ruby從入門到精通》
R 《R語言實戰》
Go 《Go語言聖經》

5. 編程學習網站推薦

網站 網址
菜鳥教程
W3School
實驗樓
猿學
慕課網
SegmentFault
博客園
GitHub
掘金
學習數據科學
易百教程
看雲

三、總結

通篇寫完,感覺自己也重新學到了很多,學習就是一個反覆複習的過程,每次學習都能帶給自己不一樣的收穫。希望以上內容可以給初入計算機相關專業的萌新帶來一些幫助,後面我會不斷更新和優化本文,請大家持續關注。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享