分类目录归档:未分类

ASP.NET Core中正确设置环境变量

一般设置都是放在appsettings.json,为了安全,特地把一些敏感信息如密码移到环境变量中去,那么如何设置带有子元素的变量呢比如ConnectStrings:

Windows下用冒号“:”分级比如:

ASPNETCORE_ConnectionStrings:BlogDb=conn_str

Linux/Mac下用双下划线“__”分级比如:

ASPNETCORE_ConnectionStrings__BlogDb=conn_str

正确的使用和清除301/302转发

Chrome, refresh with cache clear

如果没有必要请不要使用301,当你不确定的时候先用302。因为这个转发缓存是存在客户端的,一旦设置301后服务端无法通知客户端更新,除非客户自己清除缓存。

Chrome中301作为永久转发会一直保持下去,上图中就是当打开开发者工作台时可以右击刷新按钮清除缓存。

有一个特殊情况上面的方法不奏效,那就是如果是A.com转发到B.com,如果是这样就要开启工作台中的preserve log后再清除。

EntityFramework Core CLI Tools 几点坑

Entity Framework Core 2.0 Features | by Aram Koukia | Koukia

开发怎么可能离得开数据库呢~ 这里的EFC和以前的EF不是一个东西,就跟.net core和以前的.net不一样是一个道理。

Core这个家族可以说是完全跟开源接轨了,不管是运行方式,交互方式如果熟悉开源组件的同学就会感觉跟自己家一样,当然还没那么接地气,下面就是在用EFC CLI Tool时碰到的几个坑。

  1. 如果你的项目不是一个程序集,那么你不得不指定开始项目和目标项目就比如得这样:“dotnet ef list -p 目标项目目录 -s 开始项目目录”,这里我就很不理解,也困惑了我好一会,如果知道开始项目,那么作为上层程序集是可以找到下层级中程序集的dbcontext和Model。当然在文档中你可以找到这个说明,只不过我是事后才发现,试问有几个人的项目是只有一个程序集的。
  2. EF Core 6,目前是RC2,作为preview版本它默认需要你指定版本才能被安装,比如当前不指定版本他只给你装到5的latest。
  3. 需要指定一个Dbcontext,如果你有多个的话,它有一个查找顺序,是用的父类往下找的,如果碰到问题可以从这里作为突入口。
  4. 它支持从数据库导出Model,也支持从Model导入数据库,还支持从Model生成sql script。
  5. 生成scrpt最实用,这里推荐带上“-i|–idempotent”参数,不带上它会默认走一个事务提交所有Model,带上后每个Model都是分开的适用于没有严格migrations的项目。用–no-transactions可以关闭事务。
  6. “-p 和 -s”可以指定到项目文件或者项目目录,这就很开源范!~

Mac上使用SQL Server作为开发用数据库

SQL Server Management Studio (@mssqlserverssms) | Twitter

如果你有不得不用sqlserver的理由,并且手上只有MAC时那么这篇文章可能对你有帮助。

第一个要解决的就是数据库的GUI管理软件,SQL Server Management Studio (SSMS)肯定是没办法在Mac上跑了,这里最佳的替代就是Azure Data Studio,看名字就知道它的作用就是管理Azure上的数据,所以不管是sql server也好还是postgresql也好都可以用它管理。装好之后会发现它绝对是在vs code同一个team弄出来的,连很多默认样式都一样,所以跟vs code一样在使用前你得装好一些必须的插件,这里不得不说sql admin包既然是没个人必装的,为啥不直接默认安装呢。装好后像下面这个样子。

Customize Azure Data Studio with Dashboard Widgets

有的同学在没有安装推荐的admin包前会发现不能创建新的数据库,没错,这种基本插件它默认也没装,对了,可能需要FQ。

另外它也提供profile等调试插件,工具还是很丰富的,虽然离ssms还有差距,如果是专职DBA就用回SSMS吧。

VisualStudio Code Remote 调试方法(错误Containers Docker version 17.12.0 or later required.)

Remote Development with Visual Studio Code

最近在配置.net的remote开发环境,用了整套VS Code Remote的三大件,在用container时总是提示:

Remote - Containers Docker version 17.12.0 or later required.

一头雾水,docker版本20了都,可能的理解就是它没找到docker。可是信息有限,好在开源项目有issue,作者介绍了debug的办法,适用于进一步调试。

When you see this, could you click Cancel and then check the dev container log (F1 > Remote-Containers: Show Container Log) and the dev console (Help > Toggle Developer Tools) and post these here?

Developer Tools就是chrome的开发栏,是同一个东西,能够看到UI的日志,已经发现就是Docker version这个命令的返回值出错,作者在issue中反复提到会不会是没加入Path,我不信。最后他终于提到了,如果再vs code 运行中更新过docker就会出现这个bug,解决的办法就是在更新docker后重启一下vs code即可。

另外还有的碰到这个问题可以尝试通过更新vs code和docker desktop到最新的版本。

如果还是不可以除了打开日志查看报错外,可以尝试查看docker context,看看当前的context是不是default。

AWS Kubernetes/k8s kubeamd 初始化后kube-controller-manager pod CrashLoopBackOff 错误启动不了

问题出现在版本1.22和1.21中,同样的配置在1.19和1.20版本中成功配置没有问题。

kubeadm init
初始化后提示成功,在master第二个节点 kubeadm join时提示
could not find a JWS signature

于是回到第一个master上看一下cluster info信息
kubectl get configmap cluster-info --namespace=kube-public -o yaml
非常奇怪的是没有jws段,jws是一个证书的签名用来验证证书的token,这里提一下它并不安全最好不要用于所有节点,可以通过kubeadm create token xxx来创建。
没有jws段那么判断没有生效的token,但是用

kubeadm token list
可以看到token全部正常有效,这个问题就很奇怪了。

在阅读bootstrap-tokens鉴权kubeadm实现细节后发现,原来cluster info中的aws需要在kube-controller-manager运行后创建。

这时才发现kube-controller-manager的pod没有起来,我们知道kubeadm文档中说过如果init后pod有没有成功生效的那么就要发issue证明是kubeadm坏了,这个判断大概率不成立,肯定是kubeadm配置错误。

kubectl describe kube-controller-manager -nkube-system
kubectl logs -n kube-system kube-controller-manager

后提示:
Error: unknown flag: --horizontal-pod-autoscaler-use-rest-clients

原来1.21的kube-controller-manager不再支持这个参数。
去掉后pod成功启动

阿里云 NAS OSS 云盘价格对比 GB/小时

对比需要统一单位,这里以最小计费单位GB每小时来统一度量单位,这里对有的产品不公平比如按量付费和包月两种方式的,希望对产品选购也有意义。

NAS

最方便的使用方式,以每个月最大的存储上限计费对于存储波动大的很不公平。它的优点是灵活,缺点是io较低,依赖网路性能。性能型非常贵,指标只是提高了一倍,如果真的对io要求高的产品不建议使用nas。

通用型 0.35元/GiB/月=0.00048611/gh

性能型 0.35元/GiB/月+1.85元/GiB/月=0.00305556/gh

OSS

这个产品严格来说不适合作为横向对比,应用场景重合度不高,虽然它最便宜,可是明显这是个诱惑你的理由,为了赚流量费。

0.12元/GB/月=0.00016667/gh

云盘ESSD

默认的网盘,io比ssd低收费却一样,存储按量很贵,包月还说得过去。只有对io有较高需求的适合放在essd云盘中,这里推荐考虑ssd盘,只有ssd满足不了再上essd。

按量0.0420 /时/40g=0.00105/gh

包月0.00069444/gh

云盘高效云盘

最早的云盘,io不是特别高的可以考虑。

包月0.00048611/gh

kubernetes/k8s pod下多容器的设计模式(ambassador 大使代理模式,adapter 适配模式,sidecar 边车模式, init containers初始化容器)

英文好的可以直接阅读原文:引用原文(英文):https://learnk8s.io/sidecar-containers-patterns

TL;TR:k8s patterns包含了云原生架构中各种的最佳实践,这里面绕不开用的最多的就是pod下多容器的pattern,也是k8s与swarm区别最大的地方。利用好这些pattern可以在不修改任何代码的情况下实现不同的行为比如TLS加固。

k8s把最小单位从容器上升到了pod是它设计的核心思想,这种设计带来了与原生docker容器无法比拟的优势,我们知道容器利用了linux下的各种命名空间用来隔离各种资源,但是pod作为多个容器的上一层,它可以利用命名空间是的这些容器共享某些资源从而达到亲缘性,比如共用网络、共用存储空间实现unionfile等。

示例: 一个安全的http服务

如何利用pod下多容器模式如何实现一个ElasticSearch服务的强化:

apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: elasticsearch
 spec:
   selector:
     matchLabels:
         es.test: elasticsearch
   template:
     metadata:
       labels:
         es.test: elasticsearch
     spec:
       containers:
       - name: elasticsearch
         image: elasticsearch:7.9.3
         env:
           - name: discovery.type
             value: single-node
         ports:
         - name:  http
           containerPort:  9200
 apiVersion: v1
 kind: Service
 metadata:
   name: elasticsearch
 spec:
   selector:
     es.test: elasticsearch
   ports:
 port: 9200
 targetPort: 9200 
kubectl run -it --rm --image=curlimages/curl curl \
   -- curl http://elasticsearch:9200
 {
   "name" : "elasticsearch-77d857c8cf-mk2dv",
   "cluster_name" : "docker-cluster",
   "cluster_uuid" : "z98oL-w-SLKJBhh5KVG4kg",
   "version" : {
     "number" : "7.9.3",
     "build_flavor" : "default",
     "build_type" : "docker",
     "build_hash" : "c4138e51121ef06a6404866cddc601906fe5c868",
     "build_date" : "2020-10-16T10:36:16.141335Z",
     "build_snapshot" : false,
     "lucene_version" : "8.6.2",
     "minimum_wire_compatibility_version" : "6.8.0",
     "minimum_index_compatibility_version" : "6.0.0-beta1"
   },
   "tagline" : "You Know, for Search"
 }

现在的访问是明文的,那么如何方便的使用多容器pod来实现TLS加固传输呢,如果你想到用ingress(通常用来路由外部流量到pod),这里从ingress到pod之间还是未加密的如下图:

The external traffic is routed to the Ingress and then to Pods.

那么满足zero-trust的办法就是给这个pod加入一个nginx代理tls加密流量如下图:

If you include a proxy container in the pod, you can terminate TLS in the Nginx pod.

增加一个nginx容器代理tls流量

apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: elasticsearch
 spec:
   selector:
     matchLabels:
       app.kubernetes.io/name: elasticsearch
   template:
     metadata:
       labels:
         app.kubernetes.io/name: elasticsearch
     spec:
       containers:
         - name: elasticsearch
           image: elasticsearch:7.9.3
           env:
             - name: discovery.type
               value: single-node
             - name: network.host
               value: 127.0.0.1
             - name: http.port
               value: '9201'
         - name: nginx-proxy
           image: nginx:1.19.5
           volumeMounts:
             - name: nginx-config
               mountPath: /etc/nginx/conf.d
               readOnly: true
             - name: certs
               mountPath: /certs
               readOnly: true
           ports:
             - name: https
               containerPort: 9200
       volumes:
         - name: nginx-config
           configMap:
             name: elasticsearch-nginx
         - name: certs
           secret:
             secretName: elasticsearch-tls
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: elasticsearch-nginx
 data:
   elasticsearch.conf: |
     server {
         listen 9200 ssl;
         server_name elasticsearch;
         ssl_certificate /certs/tls.crt;
         ssl_certificate_key /certs/tls.key;
     location / {         proxy_pass http://localhost:9201;     } }

前面的配置中我们利用用service可以让curl明文的访问es的接口,而这个配置中改为用nginx代理了9200,es只对localhost暴露9201,也就是从pod以外是访问不到es了。nginx在9200端口监听了https请求并转发给http的9200本地的端口给ES。

The request is proxies by Nginx on port 9220 and forwarded to port 9201 on Elastisearch

代理容器是最常用的一种Pattern

这种添加一个代理容器到一个pod的解决方式称之为:Ambassador Pattern

本文中所有模式都可以在google的研究文稿中找到详细的论述:https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/45406.pdf

添加基本的TLS还只是开始,除此之外还可以用这个模式做到以下几点:

  • 如果你希望集群上的流量都用tls证书加密,你可以在所有pod上加上一个nginx代理。或者你也可以更进一步使用mutual TLS来保证所有认证的请求已经被很好的加密,正如lstio 和linkerd在service meshes中做的。
  • 在OAuth认证中也可以使用nginx代理来保证所有请求是被jwt验证的。
  • 用在连接外部的数据库,比如一些不支持TLS或者旧版本的数据库时这也是很方便的方式。

暴露一个标准的接口用来度量

假设你已经熟悉如何使用 Prometheus来监控所有集群中的服务,但是你也正在使用一些原生并不支持 Prometheus度量的服务,比如Elasticsearch。

如何在不更改代码的情况下添加 Prometheus度量?

Adapter Pattern 适配模式

以ES为例,我们可以添加一个 exporter容器以Prometheus的格式暴露ES的度量。这非常简单,用一个开源的exporter for es 即可:

apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: elasticsearch
 spec:
   selector:
     matchLabels:
       app.kubernetes.io/name: elasticsearch
   template:
     metadata:
       labels:
         app.kubernetes.io/name: elasticsearch
     spec:
       containers:
         - name: elasticsearch
           image: elasticsearch:7.9.3
           env:
             - name: discovery.type
               value: single-node
           ports:
             - name: http
               containerPort: 9200
         - name: prometheus-exporter
           image: justwatch/elasticsearch_exporter:1.1.0
           args:
             - '--es.uri=http://localhost:9200'
           ports:
             - name: http-prometheus
               containerPort: 9114
 apiVersion: v1
 kind: Service
 metadata:
   name: elasticsearch
 spec:
   selector:
     app.kubernetes.io/name: elasticsearch
   ports:
     - name: http
       port: 9200
       targetPort: http
     - name: http-prometheus
       port: 9114
       targetPort: http-prometheus

通过这种方式可以更广泛的使用prometheus度量从而达到更好的应用与基础架构的分离。

日志跟踪 / Sidecar Pattern

边车模式,我一直把他想想成老式三轮摩托车的副座,它始终与摩托车主题保持一致并提供各种辅助功能,实现方式也是添加容器来曾强pod中应用。边车最经典的应用就是日志跟踪。

在容器化的环境中最标准的做法是标准输出日志到一个中心化的收集器中用于分析和管理。但是很多老的应用是将日志写入文件,而更改日志输出有时候是一件困难的事。

那么添加一个日志跟踪的边车就意味着你可能不必去更改日志代码。回到ElasticSearch这个例子,虽然它默认是标准输出把它写入文件有点做作,这里作为示例我们可以这样部署:

apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: elasticsearch
   labels:
     app.kubernetes.io/name: elasticsearch
 spec:
   selector:
     matchLabels:
       app.kubernetes.io/name: elasticsearch
   template:
     metadata:
       labels:
         app.kubernetes.io/name: elasticsearch
     spec:
       containers:
         - name: elasticsearch
           image: elasticsearch:7.9.3
           env:
             - name: discovery.type
               value: single-node
             - name: path.logs
               value: /var/log/elasticsearch
           volumeMounts:
             - name: logs
               mountPath: /var/log/elasticsearch
             - name: logging-config
               mountPath: /usr/share/elasticsearch/config/log4j2.properties
               subPath: log4j2.properties
               readOnly: true
           ports:
             - name: http
               containerPort: 9200
         - name: logs
           image: alpine:3.12
           command:
             - tail
             - -f
             - /logs/docker-cluster_server.json
           volumeMounts:
             - name: logs
               mountPath: /logs
               readOnly: true
       volumes:
         - name: logging-config
           configMap:
             name: elasticsearch-logging
         - name: logs
           emptyDir: {}

这里的logs容器就是sidecar的一个具体实现,现实中可以使用具体的日志收集器代替比如filebeat。当app持续写入数据时,边车中的日志收集程序会不断的以只读的形式收集日志,这里的logs边车就把写入文件的logs变为标准输出而不需要修改任何代码。

其他边车模式常用的场景

  • 实时的重启ConfigMaps而不需要重启pod
  • 从Hashcorp Vault注入秘钥
  • 添加一个本地的redis作为一个低延迟的内存缓存服务

在pod前的准备工作中使用Init Containers

k8s除了提供多容器外还提供了一种叫做初始化容器的功能,顾名思义它就是在pods 容器启动前工作的容器,我一般把它当做job这样的概念,一般场景中init containers这些容器在执行完后就不再运行了处于pause状态,这里特别要注意的是它的执行会严格按照编排的从上至下的顺序逐一初始化,这种顺序也是实现初始化工作不可缺少的。下面还是以ES为例子:

ES 文档中建议生产中设置vm.max_map_count这个sysctl属性。

这就带来了一个问题,这个属性只能在节点级别才可以被修改,容器级别是没有做到隔离。

所以在不修改k8s代码的情况下你不得不使用特权级别来运行es已达到修改的目的,而这也不是你所希望的,

因为他会带来很严重的安全问题

那么使用Init Containers就可以很好地解决这个问题,做法就是只在初始化容器中提权修改设置,那么后面的es只是普通容器就可以运行。如下:

apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: elasticsearch
 spec:
   selector:
     matchLabels:
       app.kubernetes.io/name: elasticsearch
   template:
     metadata:
       labels:
         app.kubernetes.io/name: elasticsearch
     spec:
       initContainers:
         - name: update-sysctl
           image: alpine:3.12
           command: ['/bin/sh']
           args:
             - -c
             - |
               sysctl -w vm.max_map_count=262144
           securityContext:
             privileged: true
       containers:
         - name: elasticsearch
           image: elasticsearch:7.9.3
           env:
             - name: discovery.type
               value: single-node
           ports:
             - name: http
               containerPort: 9200

除了上面这种常见的做法外初始化容器还可以这么用,当你HashicCorp Vault 来管理secrets而不是k8s secrets时,你可以在初始化容器中读取并放入一个emptyDir中。比如这样:

apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: myapp
   labels:
     app.kubernetes.io/name: myapp
 spec:
   selector:
     matchLabels:
       app.kubernetes.io/name: myapp
   template:
     metadata:
       labels:
         app.kubernetes.io/name: myapp
     spec:
       initContainers:
         - name: get-secret
           image: vault
           volumeMounts:
             - name: secrets
               mountPath: /secrets
           command: ['/bin/sh']
           args:
             - -c
             - |
               vault read secret/my-secret > /secrets/my-secret
       containers:
         - name: myapp
           image: myapp
           volumeMounts:
             - name: secrets
               mountPath: /secrets
       volumes:
         - name: secrets
           emptyDir: {}

更多的初始化容器应用场景

  • 你希望在运行app前跑数据库的迁移脚本
  • 从外部读取/拉取一个超大文件时可以避免容器臃肿

总结

这些pattern非常巧妙地用很小的代价非侵入的解决现实中常见的问题,这里要特别说明的是除了初始化容器会在运行后暂停不占用资源外,pods中增加的容器都是吃资源的,实际使用中我们不希望因为解决一个小问题反倒拖累整个pod,所以在边车这类容器组件的选择上要慎重,要足够的高效轻量,常见的像nginx、go写的大部分组件就是一个很好的选择,java写的就呵呵了。

如果希望挖掘更多多容器的设计细节可以查看官方文档:https://kubernetes.io/docs/concepts/workloads/pods/,还有google的容器设计论文:https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/45406.pdf

用docker搭建一个ElasticSearch集群并集成Kibana+cerebro可视化

JMeter (ElasticSearch + Kibana) | Blog

ES是一个非常流行的基于lucene的搜索和分析引擎,它提供了resetful的api非常适用于搜索、推荐、日志分析等场景,社区生态丰富,是分布式架构中非常重要的一环。下面从一个docker-compose部署一个最小化集群,并用Kibana和cerebro可视化的来一窥它的能力。

version: '3.9'
services:
  cerebro:
    image: lmenezes/cerebro
    container_name: cerebro
    ports:
      - "9000:9000"
    command:
      - -Dhosts.0.host=http://host.docker.internal:9200 
      # 因为我是mac所以用host.docker.internal代替主机名
  kibana:
    image: docker.elastic.co/kibana/kibana:7.7.0
    container_name: kibana7
    environment:
      - I18N_LOCALE=zh-CN
      - XPACK_GRAPH_ENABLED=true
      - TIMELION_ENABLED=true
      - XPACK_MONITORING_COLLECTION_ENABLED="true"
      - ELASTICSEARCH_HOSTS=http://host.docker.internal:9200
    ports:
      - "5601:5601"
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.7.0
    container_name: es7_01
    environment:
      - cluster.name=cnblogs
      - node.name=es7_01
      - bootstrap.memory_lock=true  
      # 避免使用swap,减少磁盘IO
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"  
      # 防止JVM内存回收
      - discovery.seed_hosts=172.19.0.5,172.19.0.4
      # 默认ES只搜索本机的其他节点,如果节点在其他主机上则需要在这里给出地址
      - cluster.initial_master_nodes=es7_01,es7_02   
      # 初始化时符合主节点的节点方便选举
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - es7data1:/usr/share/elasticsearch/data
    ports:
      - 9200:9200

  elasticsearch2:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.7.0
    container_name: es7_02
    environment:
      - cluster.name=cnblogs
      - node.name=es7_02
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - discovery.seed_hosts=172.19.0.5,172.19.0.4
      - cluster.initial_master_nodes=es7_01,es7_02
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - es7data2:/usr/share/elasticsearch/data
    expose:
      - 9200


volumes:
  es7data1:
    driver: local
  es7data2:
    driver: local

networks:
  default:
    external: true
    name: es7net

运行上述代码前请先创建一个名为es7net的网络: docker network create es7net
下面是配置集群几个常见配置的原则

  • bootstrap.memory_lock一般设置为true,避免使用swap
  • ES_JAVA_OPTS中Xms和Xmx设置为一致,避免jvm内存重新分配
  • cerebro起来比较快,kinaba很慢要等一下,多刷新就好了
  • 运行上面的集群请至少保证docker拥有3g以上,因为Elasticsearch by default deploys with a 1GB heap,开始只使用了2g时就会出现内存不足导致es启动失败,如果是下面几种错误信息可以尝试增加内存
master not discovered or elected yet, an election requires a node with id 
或
master not discovered yet, this node has not previously joined a bootstrapped (v7+) cluster, and this node must discover master-eligible nodes 
或
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

启动后可以按照以下步骤逐步检查部署

1检查ES运行状态

   http://localhost:9200/_cat/health?v
显示green表示工作正常

2 登录cerebro

cb的概览非常适合作为看板,对于刚接触es的同学来说能更好的理解es集群的状态,虽然它不是官方的

3 登录kinaba,官方

官方样例数据就可以创建非常漂亮的看板/报表

可以查看非常细节的数据,并支持完整的索引操作

从125ms到11ms,记一次关键字检测过滤服务的优化 -python and Pythonnet

接上文:《高效的的关键字查找和检测(哈希表和Trie前缀树和FastCheck)在实际使用中的性能

动态语言是很慢的,它更多的是为了提高开发效率,这里的关键字过滤算法在生产环境中用原生python达到125ms 2千万字/每秒已经够用了。那么是不是可以适当的优化再快一点?

Cython,没事cython一下,带你飞

cython能把大部分python代码改为静态的c实现,甚至你可以混合c和python一起写。不过现实是大部分的pythoner如果碰到性能问题要么用go要么就是用csharp,很少去写cython。下面是用cython编译后的测试结果:

提升39%,马马虎虎,也可能跟Tool.Good源码的实现有关。此时文本的处理量已经到了3400万/每秒,用是够用了,那么能不能更快一点?

C# 是时候展现你的实力了

Tool.Good作者明显是一位纯粹的csharpner, 既然已经实现了3亿效率的代码为什么不拿来用呢。这里作为对比,我先写了一个参照的测试程序,测试环境与前文相同。代码如下

using System;
using ToolGood.Words;
namespace KeywordTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
           
            string post = System.IO.File.ReadAllText(@"D:\Projects\Opensource\ToolGood.Words\csharp\KeywordTest\sample_post");

            string[] spams = System.IO.File.ReadAllLines(@"D:\Projects\Opensource\ToolGood.Words\csharp\KeywordTest\SpamWordsCN.min.txt");

            StringSearch iwords = new StringSearch();
            iwords.SetKeywords(spams);
            stopwatch.Start();
            for (var i = 0; i < 500; i++)
            { var f = iwords.FindFirst(post); }

            stopwatch.Stop();
            var s = stopwatch.ElapsedMilliseconds;
            Console.WriteLine("测试用时(ms):" + s);
        }
    }
}
Customer Retention: 3 Easy Ways to WOW Your Customers | Car People Marketing
此时的表情

Csharp性能都这样了更别说C了,那么为什么能比cython快那么多呢。这里分两部分原因,一部分性能浪费在python和c的交互上,另一部分在cython编译时的类型推断和生成的代码优化不够,大量使用的还是pure python。咱不纠结这个,手头有这么快的csharp不用简直浪费,一脚踢走cython,黏上csharp,让它飞的更高~

接福啦- 简书

Pythonnet 闪亮登场

这可是个好东西,.net基金会项目,官方身份,神秘可靠。有了它使得python和.net交互变得简单。这里需要一提的是目前稳定版2.5.2还只支持.net 4.0,至于.net core 3和.net5需要手动安装master分支上的3.0.0dev,实现方式 也有所不同,下面是代码:

def test2():

    from clr_loader import get_coreclr
    from pythonnet import set_runtime

    rt = get_coreclr("./runtimeconfig.json")
    set_runtime(rt)
    import  clr
    clr.AddReference('ToolGood.Words')

    import clr
    from System import String
    from ToolGood.Words import StringSearch
    stringSearch=StringSearch()

    with  open('./sample_post',encoding='utf-8') as f:
        test_post = f.read()
    from System.Collections.Generic import List
    spam_words=List[String]()
    with open('././SpamWordsCN.min.txt',encoding='utf-8') as f:
        for line in [line.rstrip() for line in f]:
            spam_words.Add(String(line))

    stringSearch.SetKeywords(spam_words)
    import time
    start = time.time()
    times = 500
    while times > 0:
        f = stringSearch.FindFirst(test_post)
        times -= 1
    end = time.time()
    print('程序运行时间:%s毫秒' % ((end - start) * 1000))
{
  "runtimeOptions": {
    "tfm": "net5.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "5.0.2"
    }
  }
}
4种吃了会让人开心的食物- 每日头条
此时的心情

事实上如果增加压力还能提高性能,因为交互部分压力越大性价比越高。此时的处理速度已经到了2.5亿/每秒。那么能不能再快点?烦不烦,其实也不烦,生产中怎么可能不并行呢,动不动八核十几核的,线程再来double一下,妥妥的几十亿。

为什么不直接用csharp直接做服务

当然可以,如果负载确实高换纯csharp顶上,还不够再换go,还不够把分布式也弄上。但现实是高负载的需求可能永远不会到来,为什么不把时间省下来去做更有意义的事呢?